home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / Storage / Bento / CM / Update.c < prev    next >
Encoding:
Text File  |  1996-08-28  |  184.8 KB  |  3,867 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:         Update.c 
  3.  
  4.     Contains:    Container Manager Container Updating Routines
  5.  
  6.     Owned by:    Ira L. Ruben
  7.  
  8.     Owned by:    Ed Lai
  9.  
  10.     Copyright:    © 1992 - 1996 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.  
  14.          <2>     8/13/96    DM        1362809: disable containers on error
  15.          <9>     10/9/95    EL        1289916: when touch data struct is freed,
  16.                                     valueHdr should no longer point to touch
  17.                                     because this is a problem in merge where
  18.                                     valueHdr is not freed and can point to
  19.                                     garbage now. 1290781: avoid generating
  20.                                     extra delete property opocde. Ignore opcode
  21.                                     if target object or property cannot be
  22.                                     found.
  23.          <8>     5/25/95    jpa        Fixed minor typecheck mistake exposed by
  24.                                                     new SC compiler. [1241078]
  25.          <7>     5/11/95    DM        1214394, 1245829: stop compiler warnings
  26.          <6>     12/9/94    EL        #1182275 Optionally do not maintain
  27.                                                     continue flag. When updates are merged,
  28.                                                     past deleted objects are gone, so we cannot
  29.                                                     depend to get them from touch list.
  30.          <5>    10/17/94    EL        #1193668 Fix bug where offsetInfoPtr in
  31.                                                     applyValueUpdateInstructions can be
  32.                                                     uninitialized.
  33.          <4>     9/30/94    EL        #1182275 rename mergeCandidate to
  34.                                                     updateMergeCandidate.
  35.          <3>     9/15/94    EL        #1182275 Add cmClosePastDataGap.
  36.          <2>     8/26/94    EL        #1182275 Merge of one updating container to
  37.                                                     another updating container. #1182308 Do not
  38.                                                     do endian-ness byte flipping for immediate
  39.                                                     data in update object.
  40.          <7>      6/3/94    EL        Rename Values.h/c to ValueRtn.h/c.
  41.          <6>      6/2/94    EL        #1156779. Fix insertion of data into
  42.                                                     immediate value.
  43.          <5>      5/5/94    EL        Post alpha fix to the bug. #1156779.
  44.          <4>     4/18/94    EL        Yet another fix to the same bug. #1156779
  45.          <3>     4/13/94    EL        Previous fix does not work for non-zero
  46.                                                     length data. #1156779
  47.          <2>     4/13/94    EL        Allow replacement of non-immediate data in
  48.                                                     target by immediate data. #1156779
  49.          <1>      2/3/94    EL        first checked in
  50.          <3>    10/29/93    EL        Add extra parameter to cmDeleteTouchList
  51.                                                     and export cmDeleteObjeFromTouchedChain
  52.          <2>    10/21/93    EL        Record delete last value as delete
  53.                                                     property. Add debug dumping of update
  54.                                                     opcodes.
  55.  
  56.     To Do:
  57.         Merging of SetInfoedValue.
  58.     In Progress:
  59.         
  60. */
  61.  
  62. /*---------------------------------------------------------------------------*
  63.  |                                                                           |
  64.  |                             <<<  Update.c  >>>                            |
  65.  |                                                                           |
  66.  |              Container Manager Container Updating Routines                |
  67.  |                                                                           |
  68.  |                               Ira L. Ruben                                |
  69.  |                                  7/10/92                                  |
  70.  |                                                                           |
  71.  |                     Copyright Apple Computer, Inc. 1992-1994              |
  72.  |                           All rights reserved.                            |
  73.  |                                                                           |
  74.  *---------------------------------------------------------------------------*
  75.  
  76.  This file includes almost all the "incremental update" processing routines when one
  77.  updating container updates its target container.  The routines in here are responsible
  78.  for maintaining the "touched chain" and "touched lists". These are additional data 
  79.  structures layered on to the main data structures (see TOCEntries.[hc]) as described in
  80.  the following (abbreviated) diagram:
  81.  
  82.  
  83.                                                                 *---------*
  84.                                                                 |   CCB   |    Touched Chain
  85.                                                                 |         |--------->---------+
  86.                                                                 *---------*                   |
  87.                                                                                                                             |
  88.                                                                                                                             |
  89.                                      +------------<-------------+               |
  90.                                      |                          |               |
  91.                     *----*<--+                          +------*----*<--+
  92.                     | O1 |          *-----*                    | O2 |          *-----*
  93.                     |    |--------->| P11 |- - ->              |    |--------->| P21 |- - ->
  94.                     *----*          *-----*                    *----*          *-----* 
  95.                         |                |                         |                |
  96.              Touched List          |                    Touched List          |
  97.                         |            *-------*                     |            *-------*
  98.              *---------*<--+   |VH(T11)|                *---------*<----->|VH(T21)|
  99.              |O1 P1 T12|   |   *-------*                |O2 P2 T21|       *-------*
  100.              | [flags] |   |       |                    | [flags] |           |
  101.              *---------*   |       |                    *---------*           |
  102.                         |        |   *-------*                     |            *-------*
  103.                         |        +-->|VH(T12)|                     |            |VH(T22)| 
  104.                         |            *-------*                     |            *-------*
  105.              *---------*           |                    *---------*<--+       |
  106.              |O1 P1 T13|           |                    |O1 P1 T12|   |       |
  107.              | removed |       '''''''''                |inserted |   |   *-------*
  108.              *---------*       'VH(T13)'                *---------*   +-->|VH(T13)|
  109.                                                  '''''''''                                  *-------*
  110.                                                        .                                          .
  111.                                                          ............................................
  112.  
  113.  
  114.  This diagram shows parts of two objects, O1 and O2, with at least one property each, P11
  115.  and P12 respectively. Each property has two value (headers) of their own of the indicated
  116.  types.  The value segments and all links details for this stuff are not shown.  Value
  117.  header VH(T13) is shown moved from O1/P11 to O2/P21.
  118.  
  119.                                 The "Touched Chain"
  120.  
  121.  This is a singly linked chain of all objects (through the TOCObjects) that are "touched"
  122.  during the updating session. The head of this chain is contained in the Container Control
  123.  Block (CCB).  The order of updating recording does not matter, so the most convenient way
  124.  to link this list is backward.  Thus in the above diagram object O1 was touched before O2.
  125.  
  126.                                                                 The "Touched List"
  127.  
  128.  This is a doubly linked list whose header is in the object (TOCObject).  A doubly linked
  129.  list is used to make it easy to remove or move these entries.  List entries are generated
  130.  for each value updated.  There is only one list entry per value no matter how many times
  131.  the corresponding value is updated.  The value header for an updated value points back to
  132.  its touched list entry.  This pointer is initially NULL and doubles as a "first touched"
  133.  switch.
  134.  
  135.  Each touched list entry contains the following:
  136.  
  137.  (a). The original object, property, and type IDs which we refer to as the "OPT address".
  138.             The OPTs will work their way into the updating instructions generated at close time
  139.             for use in addressing the original values at open time.  They uniquely address the
  140.             original value prior to applying any updates to properly apply those updates.
  141.  
  142.  (b). A pointer to the value header that was touched.  Thus the touched list entry and its
  143.             value header point to each other.
  144.  
  145.  (c). A "back pointer" to an initial touched list entry for moved values.  The use of this
  146.             pointer is discussed later.  For unmoved touched values, this pointer is NULL.
  147.  
  148.  (d). Flags to indicate special conditions of the touched entry.  These conditions are:
  149.  
  150.             -> "Removed"                        The value has been moved to another object. The touched list
  151.                                                              entry for the original "from" object's value is created or
  152.                                                              flagged as "removed".
  153.  
  154.             -> "Inserted"                        The value has been moved from another or the same object.  A
  155.                                                              touched list entry for the destination object is created and
  156.                                                              marked as "inserted". If it is the same object and previously
  157.                                                              touched, the touched list entry is changed to "inserted".
  158.  
  159.             -> "Deleted Value"            The value has been deleted.
  160.  
  161.             -> "Deleted Property"        The property specified by address "OP" has been deleted.
  162.  
  163.  Except for the "deleted property" flag, all flags deal with moved values. 
  164.  
  165.  The only things not in this file that deal with updating are the open-time and close-time
  166.  processing dealing with connecting and detaching the updating container from its target.
  167.  Most everything else is here.
  168.  
  169.  Enjoy...
  170. */
  171.  
  172. #include <stddef.h>
  173. #include <string.h>
  174. #include <stdio.h>
  175. #include <setjmp.h>
  176.  
  177. #ifndef __CMTYPES__
  178. #include "CMTypes.h"
  179. #endif
  180. #ifndef __CM_API_TYPES__
  181. #include "CMAPITyp.h"
  182. #endif
  183. #ifndef __CM_API__
  184. #include "CMAPI.h"
  185. #endif
  186. #ifndef __LISTMGR__
  187. #include "ListMgr.h"
  188. #endif
  189. #ifndef __VALUEROUTINES__
  190. #include "ValueRtn.h"       
  191. #endif
  192. #ifndef __TOCENTRIES__
  193. #include "TOCEnts.h"   
  194. #endif
  195. #ifndef __TOCOBJECTS__
  196. #include "TOCObjs.h"   
  197. #endif
  198. #ifndef __CONTAINEROPS__
  199. #include "Containr.h"  
  200. #endif
  201. #ifndef __HANDLERS__
  202. #include "Handlers.h"
  203. #endif
  204. #ifndef __FREESPACE__
  205. #include "FreeSpce.h" 
  206. #endif
  207. #ifndef __UPDATING__
  208. #include "Update.h"  
  209. #endif
  210. #ifndef __BUFFEREDIO__
  211. #include "BufferIO.h"  
  212. #endif
  213. #ifndef __SESSIONDATA__
  214. #include "Session.h"          
  215. #endif
  216. #ifndef __ERRORRPT__
  217. #include "ErrorRpt.h"      
  218. #endif
  219. #ifndef __UTILITYROUTINES__
  220. #include "Utility.h"        
  221. #endif
  222.  
  223.                                                                     CM_CFUNCTIONS
  224.  
  225. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  226. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  227. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  228. /* choke compilers that don't recognize them.  Besides why would they be looked at if        */
  229. /* it's being conditionally skipped over anyway?  I know, famous last words!                        */
  230.  
  231. #if CM_MPW
  232. #pragma segment Updating
  233. #endif
  234.  
  235. #define debugUpdateList 0
  236. #define CMIGNORE_NONFATAL 1
  237.  
  238.  
  239. struct PastUpdate {                                                /* Layout of a update which had been applied: */
  240.     ListLinks                updateLinks;                        /*    other update instr links (must be first)*/
  241.     CMObjectID            objectID;                                /*        value's original object ID                            */
  242.     CMObjectID            propertyID;                            /*          "        "     property ID                        */
  243.     CMObjectID            typeID;                                    /*          "        "     type ID                                */
  244.     CM_CHAR                    ctlByte;                                /*    type of update                                                    */
  245. };
  246. typedef struct PastUpdate PastUpdate;
  247.  
  248. struct PastDeleteData {
  249.     PastUpdate            updateHeader;                        /* The OPT of the Value                                                */
  250.     CM_ULONG                offset;
  251.     CM_ULONG                size;
  252. };
  253. typedef struct PastDeleteData PastDeleteData;
  254.  
  255. struct PastInsertData {
  256.     PastUpdate            updateHeader;                        /* The OPT of the Value                                                */
  257.     CM_ULONG                insertPoint;
  258.     CM_ULONG                insertCount;
  259.     CM_ULONG                offset;
  260.     CM_ULONG                size;
  261. };
  262. typedef struct PastInsertData PastInsertData;
  263.  
  264. struct OffsetSizePair {
  265.     CM_ULONG                offset;
  266.     CM_ULONG                size;
  267. };
  268. typedef struct OffsetSizePair OffsetSizePair;
  269.  
  270. struct PastReplaceImmediate {
  271.     PastUpdate            updateHeader;                /* The OPT of the Value                                                */
  272.     CM_ULONG                size;
  273.     CM_ULONG                newImmediate;
  274. };
  275. typedef struct PastReplaceImmediate PastReplaceImmediate;
  276.  
  277. struct PastSetInfoedValue {
  278.     PastUpdate            updateHeader;                /* The OPT of the Value                                                */
  279.     CMObjectID            newType;
  280.     CMGeneration        generation;
  281. };
  282. typedef struct PastSetInfoedValue PastSetInfoedValue;
  283.  
  284. struct PastReplaceBaseType {
  285.     PastUpdate            updateHeader;                        /* The OPT of the Value                                                */
  286.     CM_USHORT                nBases;
  287. };
  288. typedef struct PastReplaceBaseType PastReplaceBaseType;
  289.  
  290. /*---------------------------------------------*
  291.  | File Organization (yes, this file has one!) |
  292.  *---------------------------------------------*
  293.  
  294.  To aid those reading this file, and who aren't using MPW and its marker facility (if you 
  295.  don't know what markers are keep reading) the following list of function names summarizes
  296.  the grouping organization in thei file.  This may make it easier to see the breakdown of
  297.  this file to understand it.
  298.  
  299.  Miscellaneoush utilities
  300.  ------------------------
  301.      cmAddObjToTouchedChain              - add an object on to the container's touched chain
  302.      deleteObjFromTouchedChain         - delete an object from the container's touched chain
  303.      createTouchedListEntry              - create a touched list entry for a value
  304.      cmDeleteTouchedList                      - delete entire touched list for an object
  305.      
  306.  Simple Touches (touch list creation)
  307.  ------------------------------------
  308.      simpleValueTouch                         - generate touched list entry for a "simple" touch
  309.      cmTouchEditedValue                     - generate a touched list entry for an edited value
  310.      cmTouchImmediateValue                  - generate a touched list entry for a modified immediate
  311.      cmTouchBaseType                              - generate a touched list entry for a modified base type
  312.      cmTouchSetInfoedValue                  - generate a touched list entry for a "set-infoed" value
  313.      
  314.  Complex Touches (touch list creation)
  315.  -------------------------------------
  316.      cmTouchMovedValue                         - generate touched lists for a value being moved
  317.      cmTouchDeletedValue                    - generate touched list entry for value being deleted
  318.      cmImplicitDeleteValueTouch         - delete touched list entry for an implicitly deleted value
  319.      cmTouchDeletedProperty             - generate a touched list entry for a deleted property
  320.      cmTouchDeletedObject                      - generate a touched list entry for a deleted object
  321.      
  322.  Update Instruction Generation (output)
  323.  --------------------------------------
  324.      generateDataEdits                         - generate insert/delete (data editing) instructions
  325.      genValueUpdateInstructions     - generate value instructions
  326.      generateValueUpdates                  - main control generate value instructions
  327.      generateDeleteUpdates               - generate object and property delete instructions
  328.      cmGenerateUpdates                        - main control for update instruction generation
  329.      
  330.  Update Instruction Interpretation (input)
  331.  -----------------------------------------
  332.      applyValueUpdateInstructions - value updating instruction interpreter
  333.      applyValueUpdates                        - main control interpreter to apply value updating instructions
  334.      applyDeleteUpdates                      - interpreter to apply object/property deletion instructions
  335.      cmApplyUpdates                              - apply all updates to bring a target "up to date"
  336. */
  337.  
  338.     
  339. /*------------------------*
  340.  | Updating Control Codes |
  341.  *------------------------*/
  342.  
  343. /* The following define the control code values generated in the updating instructions    */
  344. /* written to the container.  See code that generates the actual data for the exact         */
  345. /* instruction format layout.                                                                                                                      */
  346.  
  347. #define EndUpdates            0xFFU                                /* end of updating instruction sequence            */
  348.                                                                                         
  349.                                                                                         /* ***The following are main control codes***/
  350. #define NewProperty                 1U                                /* new property (type follows)                            */
  351. #define NewType                         2U                                /* new type, same prop. (value ctl follows)    */
  352. #define DeleteObject             3U                                /* delete entire object                                            */
  353. #define DeleteProperty1         4U                                /* delete property within object                        */
  354. #define DeleteProperty2         5U                                /* delete property for current object                */
  355.      
  356.                                                                                          /* ***The following are value update codes***/
  357. #define RemovedValue             6U                                /* remove value from object                                    */
  358. #define InsertedValue             7U                                /* insert a value from somplace into object    */
  359. #define SetInfoedValue         8U                                /* change type and generation                                */
  360. #define DeleteData                 9U                                /* delete data                                                            */
  361. #define InsertData1                10U                                /* insert data (one segment)                                */
  362. #define InsertDataN                11U                                /* insert data (N segments)                                    */
  363. #define ReplaceImmediate    12U                                /* replace with new immediate data                    */
  364. #define ReplaceBaseType        13U                                /* replace with new base type data                    */
  365. #define DeleteValue                14U                                /* delete value                                                            */
  366.     
  367. #define ErrorCtl                    15U                                /* used only for error recover during input    */
  368.  
  369.  
  370. /*----------------------------------------------------------------------------*
  371.  | cmAddObjToTouchedChain - add an object on to the container's touched chain |
  372.  *----------------------------------------------------------------------------*
  373.  
  374.  The TOCObject pointed to by theObject is "touched", i.e., it is put on the updating
  375.  container's singly linked chain of touched objects, the touchedChain.
  376.  
  377.  The container is passed because the touched list has to be attached to a "know fixed"
  378.  container.  The fixed container is usually the updatingContainer.  But this needs to be
  379.  "controlled" during open time because the updatingContainer is also used define the
  380.  "owner" of NEW objects read in during TOC loading.  The updatingContainer then is the
  381.  container doing the loading, NOT the original container the user opened.
  382.  
  383.  cmReadTOC(), who loads the TOC, calls this routine (which is why it is "exposed" outside
  384.  this file) to add objects to the touched chain that have the special "updates" property.
  385.  Such objects have associated updating instructions (generated by generateValueUpdates()
  386.  at close time). These must be chained together until the TOC is fully read in.  This is
  387.  because "inserted" (i.e., move) updates could refer to objects not yet  read in.  The
  388.  touched chain is a convenient place!  So we use it and this routine to build the chain.
  389.  This chain is only temporary.  It is "fed" to cmApplyUpdates() who walks the chain to
  390.  apply all the updates. The chain is then immediately cleared ready to receive new updates
  391.  in the "normal" way.
  392.  
  393.  Note, the object is placed on the touched chain only once.  This routine is, however,
  394.  called whenever we think we need to touch the object.  We worry about the multiple
  395.  touches here.
  396. */
  397.  
  398. void cmAddObjToTouchedChain(ContainerPtr container, TOCObjectPtr theObject)
  399. {
  400.     if ((theObject->objectFlags & TouchedObject) == 0) {            /* if this is 1st touch...    */
  401.         theObject->objectFlags |= TouchedObject;                                /* ...mark object as touched*/
  402.         theObject->nextTouchedObject = container->touchedChain;    /* ...link object into chain*/
  403.         container->touchedChain             = theObject;
  404.     }
  405. }
  406.  
  407. /*---------------------------------------------------------------------------------*
  408.  | deleteObjFromTouchedChain - delete an object from the container's touched chain |
  409.  *---------------------------------------------------------------------------------*
  410.  
  411.  The TOCObject pointed to by theObject is "untouched", i.e., it is removed from the
  412.  updating container's touched chain.  The pointer to the previous object on this chain is
  413.  also passed.  If it is NULL, then the previous value is assumed to be the touchedChain
  414.  list header in the updating container.
  415.  
  416.  The container is passed as an explicit parameter for the same reason it is in 
  417.  cmAddObjToTouchedChain().  See its comments for details.
  418.  
  419.  Note, the object is removed only once.  A untouched object has its touched chain link 
  420.  set to NULL.  This routine resets the link to NULL and makes sure we don't try to unlink 
  421.  it twice.
  422.  
  423.  Just as a reminder in case it's not clear how to use this thing, in particular, how to
  424.  manage thePrevObject...
  425.  
  426.  The touched chain is a singly linked list whose header, touchedChain, is in the container
  427.  control block.  If an object is to be removed from from the chain, thePrevObject should
  428.  KEEP its current value.  This could be NULL as long as the entries starting with the
  429.  first continue to be deleted.  As soon as one is retained, then thePrevObject should be
  430.  set to point at that one.
  431.  
  432.  In summary, don't change thePrevObject if this routine is called, and set thePrevObject
  433.  to each retained object at the time it's encountered.  Enough said...
  434.  
  435.  This has been enhanced so that if you don't know thePrevObject, just pass in NULL and it
  436.  would search the link list for the previous object. Since this would not be efficent it
  437.  it better off to remember thePrevObject if you can.
  438. */
  439.  
  440. void cmDeleteObjFromTouchedChain(ContainerPtr container,
  441.                                                                                             TOCObjectPtr theObject,
  442.                                                                                             TOCObjectPtr thePrevObject)
  443. {
  444.     if (thePrevObject == NULL)                                                             /* ...unlink it from chain    */
  445.         if (container->touchedChain == theObject)
  446.             container->touchedChain = theObject->nextTouchedObject;
  447.         else {
  448.             thePrevObject = container->touchedChain;
  449.             while (thePrevObject) {
  450.                 if (thePrevObject->nextTouchedObject == theObject) {
  451.                     thePrevObject->nextTouchedObject = theObject->nextTouchedObject;
  452.                     break;
  453.                 } else
  454.                     thePrevObject = thePrevObject->nextTouchedObject;
  455.             }
  456.         }
  457.     else if (thePrevObject->nextTouchedObject == theObject)
  458.         thePrevObject->nextTouchedObject = theObject->nextTouchedObject;
  459.     
  460.     theObject->nextTouchedObject = NULL;                                         /* ...(un)mark as touched    */
  461.     theObject->objectFlags      &= ~TouchedObject;
  462.  
  463. }
  464.  
  465.  
  466. /*-------------------------------------------------------------------*
  467.  | createTouchedListEntry - create a touched list entry for a value  |
  468.  *-------------------------------------------------------------------*
  469.  
  470.  A touched list entry for the value's object is created for the first touch of the
  471.  specified value (header).  The value header is set to point at the touched list entry and
  472.  vice versa.  The original object, property, and type ID (the "OPT address") are saved in
  473.  the touched list entry as well.  The touchFlags are initialized to 0 and must be set by
  474.  the caller.
  475.  
  476.  The function returns the pointer to the value's corresponding touched list entry.  NULL
  477.  is returned, and an error reported if there is an allocation error for a new touched
  478.  list entry.
  479.  
  480.  Note, if there were no previous touch list entries for the object, the object is put on
  481.  the updating container's touched chain.  If the value was previously touched, the pointer
  482.  to the already existing touched list entry is returned.
  483.  
  484.  Also note, this routine is only for creating touched list entries for values (headers).
  485.  The case for a deleted property touch is handled separately in cmTouchDeletedProperty().
  486. */
  487.  
  488. static TouchedListEntryPtr CM_NEAR createTouchedListEntry(TOCValueHdrPtr theValueHdr)
  489. {
  490.     ContainerPtr                 container;
  491.     TOCObjectPtr                 theObject;
  492.     TouchedListEntryPtr touch;
  493.     
  494.     if (theValueHdr->touch != NULL)                             /* if not first touch...                                */
  495.         return (theValueHdr->touch);                                /* ...return corresponding touched entry*/
  496.     
  497.     container = theValueHdr->container->updatingContainer; /* allocate new touched entry    */
  498.     
  499.     if ((touch = (TouchedListEntryPtr)CMmalloc(sizeof(TouchedListEntry))) == NULL) {
  500.         Container_Disable(container);
  501.         ERROR1(CM_err_NoTouchedEntry, CONTAINERNAME);
  502.         return (NULL);
  503.     }
  504.     
  505.     theValueHdr->touch = touch;                                        /* point hdr at its touched entry                */
  506.     theObject = theValueHdr->theProperty->theObject;
  507.     
  508.     cmNullListLinks(touch);                                                /* init touched list entry fields...        */
  509.     touch->theValueHdr     = theValueHdr;                        
  510.     touch->objectID         = theObject->objectID;
  511.     touch->propertyID     = theValueHdr->theProperty->propertyID;
  512.     touch->typeID             = theValueHdr->typeID;
  513.     touch->touchFlags        = 0;
  514.     touch->removedEntry = NULL;
  515.     
  516.     cmAddObjToTouchedChain(container, theObject);    /* make sure obj. is on touched chain        */
  517.  
  518.     cmAppendListCell(&theObject->touchedList, touch); /* add touch to the object                    */
  519.     
  520.     return (touch);                                                                /* return ptr to new touch list entry        */
  521. }
  522.  
  523.  
  524. /*----------------------------------------------------------------*
  525.  | cmDeleteTouchedList - delete entire touched list for an object |
  526.  *----------------------------------------------------------------*
  527.  
  528.  This routine is only used when all TOC data structures are being freed.  For each object
  529.  passed, that object's entire touched list (if any) is freed.  Since all TOC entities are
  530.  being freed, there is no need to remove the object from the touched chain since the
  531.  objects on the chain will be freed.
  532.  
  533.  Note, that the object's passed are just objects picked up during the freeing of all the
  534.  objects.  Therefore the touched chain is not being followed in the context used here.
  535.  
  536.  We pass in the container as a parameter and not derive the container from the object
  537.  because during updating, an object->container is in the target container, but the
  538.  updating container toc contains that object. When the container is closed, the target
  539.  container is closed and disposed first, so when the target container is closed and
  540.  call cmDeleteTouchedList, object->container is no longer valid.
  541.  
  542. */
  543.  
  544. void cmDeleteTouchedList(TOCObjectPtr theObject, ContainerPtr container)
  545. {
  546.     TouchedListEntryPtr nextTouch, touch;
  547.  
  548.     if (theObject->objectFlags & TouchedObject) {            /*if object was touched...                     */
  549.         theObject->objectFlags &= ~TouchedObject;                /* ...unmark just to be safe!                */
  550.         touch = (TouchedListEntryPtr)cmGetListHead(&theObject->touchedList);
  551.         
  552.         while (touch != NULL) {                                                    /* ...loop through entire touch list*/
  553.             nextTouch = (TouchedListEntryPtr)cmGetNextListCell(touch);
  554.             if (touch->theValueHdr)
  555.                 touch->theValueHdr->touch = NULL;
  556.             CMfree(cmDeleteListCell(&theObject->touchedList, touch)); /* ...free each entry        */
  557.             touch = nextTouch;
  558.         } /* while */
  559.     } /* TouchedObject */
  560. }
  561.  
  562.  
  563. /*-------------------------------------------------------------------------------------*
  564.  | simpleValueTouch - generate touched list entry for a value touched in specified way |
  565.  *-------------------------------------------------------------------------------------*
  566.  
  567.  This routine defines a touched list entry for a value. A touched list entry for the object
  568.  "owning" the value is defined.  It is the common internal routine to cmTouchEditedValue(),
  569.  cmTouchImmediateValue(), cmTouchBaseType(), and cmTouchSetInfoedValue().  These are all
  570.  "simple" touches in the sense that the touched list entry is or'ed with a flag appropriate
  571.  to the caller.  The flag is passed as the parameter.  The touched list entry for the
  572.  object "owning" a value is defined if necessary. 
  573.  
  574.  The function returns true if everything went ok and false otherwise.  This can only fail
  575.  because of an allocation error creating a touched list entry.
  576. */
  577.  
  578. static CMBoolean CM_NEAR simpleValueTouch(TOCValueHdrPtr theValueHdr, CM_USHORT flag)
  579. {
  580.     ContainerPtr                 container = theValueHdr->container->updatingContainer;
  581.     TouchedListEntryPtr touch;
  582.  
  583.     /* Don't create touched list entries if we're not updating or we're processing a new    */
  584.     /* value header...                                                                                                                                        */
  585.     
  586.     if (!TouchIt(container, theValueHdr->container))
  587.         return (true);
  588.     
  589.     /* If the value has never been touched, create a "modified" touched list entry for it */
  590.     
  591.     touch = theValueHdr->touch;                                                    /* not NULL if previously touched    */
  592.     
  593.     if (touch == NULL) {                                                                /* if this is the first touch...    */
  594.         touch = createTouchedListEntry(theValueHdr);            /* ...create "modified" entry            */
  595.         if (touch == NULL) return (false);
  596.     }
  597.     
  598.     touch->touchFlags |= flag;                                                    /* OR in the appropriate flag            */
  599.         
  600.     return (true);                                                                            /* return success                                    */
  601. }
  602.  
  603.  
  604. /*------------------------------------------------------------------------*
  605.  | cmTouchEditedValue - generate a touched list entry for an edited value |
  606.  *------------------------------------------------------------------------*
  607.  
  608.  This routine defines a touched list entry for a value when it is edited.  A touched list
  609.  entry for the object "owning" the value is defined.  The parameter specifies the value
  610.  header being edited. The function returns true if everything went ok and false otherwise.
  611.  This can only fail because of an allocation error creating a touched list entry.
  612. */
  613.  
  614. CMBoolean cmTouchEditedValue(TOCValueHdrPtr theValueHdr)
  615. {
  616.     return (simpleValueTouch(theValueHdr, TouchedEdited));    /* touch as "edited"                    */
  617. }
  618.  
  619.  
  620. /*--------------------------------------------------------------------------------------*
  621.  | cmTouchImmediateValue - generate a touched list entry for a modified immediate value |
  622.  *--------------------------------------------------------------------------------------*
  623.  
  624.  This routine defines a touched list entry for an immediate value when it is edited.  A
  625.  touched list entry for the object "owning" the value is defined.  The parameter specifies
  626.  the value header of the immediate value being edited.  The function returns true if
  627.  everything went ok and false otherwise. This can only fail because of an allocation error
  628.  creating a touched list entry.
  629. */
  630.  
  631. CMBoolean cmTouchImmediateValue(TOCValueHdrPtr theValueHdr)
  632. {
  633.     return (simpleValueTouch(theValueHdr, TouchedImmediate));    /* touch as "immediate"            */
  634. }
  635.  
  636.  
  637. /*--------------------------------------------------------------------------*
  638.  | cmTouchBaseType - generate a touched list entry for a modified base type |
  639.  *--------------------------------------------------------------------------*
  640.  
  641.  This routine defines a touched list entry for an base type when it is changed.  A touched
  642.  list entry for the object "owning" the base type (value) is defined.  The parameter
  643.  specifies the value header of the base type value being changed.  The function returns
  644.  true if everything went ok and false otherwise.  This can only fail because of an
  645.  allocation error creating a touched list entry.
  646. */
  647.  
  648. CMBoolean cmTouchBaseType(TOCValueHdrPtr theValueHdr)
  649. {
  650.     return (simpleValueTouch(theValueHdr, TouchedBaseType));    /* touch as "base type"            */
  651. }
  652.  
  653.  
  654. /*--------------------------------------------------------------------------------*
  655.  | cmTouchSetInfoedValue - generate a touched list entry for a "set-infoed" value |
  656.  *--------------------------------------------------------------------------------*
  657.  
  658.  This routine defines a touched list entry for an value when its type or generation number
  659.  is changed (i.e., it's "set-infoed").  A touched list entry for the object "owning" the
  660.  value is defined. The parameter specifies the value header of the value being set-infoed.
  661.  The function returns true if everything went ok and false otherwise.  This can only fail
  662.  because of an allocation error creating a touched list entry.
  663.  
  664.  Warning: The call to this routine MUST be done before any changes to the type because the
  665.                     touch list info is in terms of the original object, property, and type.
  666. */
  667.  
  668. CMBoolean cmTouchSetInfoedValue(TOCValueHdrPtr theValueHdr)
  669. {
  670.     return (simpleValueTouch(theValueHdr, TouchedSetinfoed));    /* touch as "set-infoed"        */
  671. }
  672.  
  673.  
  674. /*----------------------------------------------------------------------------*
  675.  | cmTouchMovedValue - generate touched lists for a value that is being moved |
  676.  *----------------------------------------------------------------------------*
  677.  
  678.  This routine defines touched list entries for a value move when a CMMoveValue() is done.
  679.  Touched lists entries for the "from" and "to" (source and destination) objects involved
  680.  in the moved value are defined.  The parameters specify the value header to be moved
  681.  (theFromValueHdr), the source ("from") TOCObject (theFromObject), the destination ("to")
  682.  TOCObject (theToObject), and destination property ID (toPropertyID).  The function
  683.  returns true if everything went ok and false otherwise.  This can only fail because of an
  684.  allocation error creating a touched list entry.
  685.  
  686.  Warning: The call to this routine MUST be done before the value is moved because the
  687.                     touch list info is in terms of the original object, property, and type.  The
  688.                     move will obviously change the object and/or property.
  689.  
  690.  Note, that a move only moves the value header passed in here.  The segments "go along for
  691.  the ride".  By moving the value header, the user's refNum to it remains valid. 
  692.  
  693.  The source and destination object's touched lists are modified "appropriately".  The
  694.  algorithm is sufficiently complex as to warrent outlining here.  Indeed, it is
  695.  sufficiently complex, that the only way to describe it is as a "finite state machine".
  696.  The following is a "state-transition" table describing the FSM.  Read states on the left,
  697.  conditions across the top.  The intersections are in the form "X/s".  This says, "execute
  698.  action X and go to state number s".  The special action "NOP" means just go to the
  699.  indicated state. 
  700.  
  701.                                                                              CMMoveValue(from current location to ...)
  702.                          "State"                                                                         
  703.            Current Location         |Orig O,Orig P|Orig O,Diff P|Same O,Diff P|   Diff. O   |
  704.  =================================|=============|=============|=============|=============|
  705.  Initial state                               0 |    NOP/0    |     A/1     |     A/1     |     B/2     |
  706.  Orig. Obj., different Property 1 |     C/0     |    NOP/1    |    NOP/1    |     D/2     |
  707.  Different 0bject                    2 |     E/0     |     F/1     |    NOP/2    |     G/2     |
  708.  ==========================================================================================
  709.  
  710.  Note, in this table, "O" and "P" stand for object and property respectively.  Also,
  711.  "Orig O" and "Same O" are identical while in states 0 or 1.  Here are the actions:
  712.  
  713.     A: if (no "from" touched list entry) create it
  714.          flag "from" touched list entry as "inserted"
  715.                                  
  716.     B: if (no "from" touched list entry) create it
  717.          flag "from" touched list entry as "removed"
  718.          create "to" touched list entry as "inserted" (set value hdr to point to this)
  719.          set back ptr in "to" "inserted" entry to point to "from" "removed" entry
  720.          
  721.     C: remove "inserted" flag from "from" touched list entry ("from" == "to" object)
  722.          if (touched value not edited or set-infoed)
  723.              delete "from" touched list entry
  724.          
  725.     D: move "from" touched list ("inserted") entry to "to" object
  726.          create "from" touched list entry as "removed"
  727.          set back ptr in "to" "inserted" entry to point to "from" "removed" entry
  728.          
  729.     E: move "from" touched list ("inserted") entry to "to" (original) object
  730.          delete "removed" entry in "to" touched list
  731.          remove "inserted" flag from "from" touched list entry
  732.          if (touched value not edited or set-infoed)
  733.              delete "from" touched list entry
  734.     
  735.     F: move "from" touched list ("inserted") entry to "to" (original) object
  736.          delete "to" "removed" touched list entry
  737.     
  738.     G: move "from" touched ("inserted") list entry to "to" object
  739.              
  740.  State 0 occurs when an object has never moved (or was moved back to it's original
  741.  position).  This is the initial state.  There could still be a touched list entry for 
  742.  the value if it was edited or set-infoed.
  743.  
  744.  State 1 is used when an value is moved to or within the same original object.  This 
  745.  can be from another object (moved there from the original object by an earlier move) 
  746.  within the original object, but from a different property (again from an earlier move).
  747.  
  748.  State 2 is entered when a value is moved to a different object.
  749.  
  750.  The general "gist" of this whole thing is to generate a "removed" entry at the original
  751.  source and an "inserted" at the destination.  The "removed" entry is suppressed when
  752.  the "inserted" is for the same original object.  Both "removed" and "inserted" are
  753.  suppressed when a value is moved back to its original object and property.
  754.  
  755.  Note, an actual state number is not maintained in the touched list entries.  Instead we
  756.  infer the state by current conditions.
  757. */
  758.  
  759. CMBoolean cmTouchMovedValue(TOCValueHdrPtr theFromValueHdr, TOCObjectPtr theFromObject,
  760.                                                         TOCObjectPtr theToObject, CMObjectID toPropertyID)
  761. {
  762.     ContainerPtr                 container = theFromValueHdr->container->updatingContainer;
  763.     TouchedListEntryPtr fromTouch, toTouch;
  764.     CMBoolean                          moveWithinSameObject;
  765.     
  766.     /* Don't create touched list entries if we're not updating or we're processing a new    */
  767.     /* value header...                                                                                                                                        */
  768.     
  769.     if (!TouchIt(container, theFromValueHdr->container))
  770.         return (true);
  771.     
  772.     /* We can tell if we're in the initial state (0) by whether the value was ever                 */
  773.     /* previously touched.  If it wasn't we know it's the first touch.  If it was, then        */
  774.     /* if the touched list entry is not flagged as "inserted" we still have the initial        */
  775.     /* move state.  It is touched because it was edited or set-infoed.                                        */
  776.     
  777.     /* Actions "A" and "B" occur here; a "from" touched list entry is created and flagged    */
  778.     /* as "inserted" or "removed".  If "removed", a "to" "inserted" touch list entry is     */
  779.     /* created.                                                                                                                                                        */
  780.     
  781.     fromTouch = theFromValueHdr->touch;                                            /* not NULL if already touched*/
  782.     moveWithinSameObject = (CMBoolean)(theFromObject==theToObject);/*moving within same obj*/
  783.     
  784.     if (fromTouch == NULL || (fromTouch->touchFlags & TouchedInserted) == 0) {
  785.         if (fromTouch == NULL) {                                                    /* create "from" list entry                */
  786.             fromTouch = createTouchedListEntry(theFromValueHdr);
  787.             if (fromTouch == NULL) return (false);
  788.         }
  789.         
  790.         if (moveWithinSameObject)                                                    /* "Orig O,Diff P"...                            */
  791.             fromTouch->touchFlags |= TouchedInserted;                /* ...action "A"                                    */
  792.         else {                                                                                        /* "Diff. O"...                                        */
  793.             if ((toTouch = (TouchedListEntryPtr)CMmalloc(sizeof(TouchedListEntry))) == NULL) {
  794.                 Container_Disable(container);
  795.                 ERROR1(CM_err_NoTouchedEntry, CONTAINERNAME);
  796.                 return (false);
  797.             }
  798.             *toTouch = *fromTouch;                                                    /* create "to" list entry                    */
  799.             cmAppendListCell(&theToObject->touchedList, toTouch);
  800.             
  801.             cmAddObjToTouchedChain(container, theToObject);    /* put "to" obj. on touched chain    */
  802.             
  803.             toTouch->touchFlags       |= TouchedInserted;            /* flag "to" entry as "inserted"    */
  804.             fromTouch->touchFlags     = TouchedRemoved;                /* flag "from" as "removed"                */
  805.             toTouch->removedEntry     = fromTouch;                        /* set back ptr to "from" entry        */
  806.             theFromValueHdr->touch    = toTouch;                            /* point value hdr at "inserted"    */
  807.             fromTouch->removedEntry = NULL;                                    /* make sure of "from" back ptr        */
  808.         }
  809.         
  810.         return (true);                                                                        /* end of actions "A"/"B"; state 0*/
  811.     } /* 1st move */
  812.     
  813.     /* At this point only states 1 and 2 are possible, i.e., the object has been moved.        */
  814.     /* Unlike the actions listed in the comments some common parts can be factored out.        */
  815.     /* The first thing to factor is the moving of the "inserted" entry to the "to" object    */
  816.     /* for actions "D", "E", "F", and "G". It is unnecessary to move the "inserted" entry    */
  817.     /* if we're moving within the same object.  The "inserted" entry will still point to     */
  818.     /* the moved value header whether moved or not moved.                                                                    */
  819.     
  820.     if (!moveWithinSameObject) {                                                /* factor "D", "E", "F", "G"...        */
  821.         cmDeleteListCell(&theFromObject->touchedList, fromTouch);    /* move "inserted" to "to"*/
  822.         cmAppendListCell(&theToObject->touchedList, fromTouch);        
  823.         cmAddObjToTouchedChain(container, theToObject);        /* put "to" obj. on touched chain    */
  824.     }                                                                                                                        
  825.     
  826.     /* At this point the "inserted" entry is considered moved to the "to" object.  We now */
  827.     /* have to see if we're in state 2 or 3.  It will be 3 if the "to" object ID is             */
  828.     /* different from the original object ID of the value which was put in the value's        */
  829.     /* touched list entry when it was first created.                                                                            */
  830.     
  831.     /* For moving to a different object, we must check that the "from" is in the original */
  832.     /* object.  If it is, there is no "removed" touched list entry.  We therefore must        */
  833.     /* create it at set the back pointer of the "to" "inserted" entry to point to it.            */
  834.     
  835.     toTouch = fromTouch;                                                                /* "to" now has "inserted" entry    */
  836.     
  837.     if (toTouch->objectID != theToObject->objectID) {        /* "Diff. O" ==> "D" or "G"                */
  838.         if (toTouch->removedEntry == NULL) {                            /* coming from state 0 ("B")==>"D"*/
  839.             if ((fromTouch = (TouchedListEntryPtr)CMmalloc(sizeof(TouchedListEntry))) == NULL) {
  840.                 Container_Disable(container);
  841.                 ERROR1(CM_err_NoTouchedEntry, CONTAINERNAME);
  842.                 return (false);
  843.             }
  844.             *fromTouch = *toTouch;                                                    /* create "from" list entry                */
  845.             cmAppendListCell(&theFromObject->touchedList, fromTouch);
  846.                 
  847.             fromTouch->touchFlags     = TouchedRemoved;                /* flag "from" as "removed"                */
  848.             toTouch->removedEntry      = fromTouch;                        /* set back ptr to "from" entry        */
  849.             fromTouch->removedEntry = NULL;                                    /* no back ptr in "removed" entry    */
  850.         }
  851.         
  852.         return (true);                                                                        /* end of actions "D"/"G"                    */
  853.     }
  854.     
  855.     /* States 2 and 3 are still in effect here.  But now we also know that we're moving     */
  856.     /* the value back to the original object or we're already there.  Thus actions "C",     */
  857.     /* "E", and "F" are possible.  So are the NOP actions.  For action "F" there is a         */
  858.     /* back pointer to the "removed" entry.  There is no "removed" entry when there is an */
  859.     /* "inserted" in the original object.  Thus it is deleted first...                                        */
  860.     
  861.     if (toTouch->removedEntry != NULL) {                                /* action "F" ==> delete "removed"*/
  862.         CMfree(cmDeleteListCell(&theToObject->touchedList, toTouch->removedEntry));
  863.         toTouch->removedEntry = NULL;                                            /* no back ptr any more                        */
  864.     }
  865.     
  866.     /* If we're moving back to the original object and property, then put things back to    */
  867.     /* the initial state (action "E").  The "inserted" entry is all that remains in the        */
  868.     /* "to" object.  Thus we play around with it.  If we're not moving back to the                 */
  869.     /* original property, then we've completed actions "C" and "F" and there is nothing     */
  870.     /* else to do.  Note that the "initial state" may not be quite as it was when the            */
  871.     /* value was moved that first time.  It could have been edited along the way.  It         */
  872.     /* could also have been edited before the initial move.  Either way, if that is the        */
  873.     /* case, we must keep a touched list entry.                                                                                        */
  874.     
  875.     if (toTouch->propertyID == toPropertyID) {                    /* moving back to orig. property    */
  876.         toTouch->touchFlags &= ~TouchedInserted;                    /* remove "inserted" flag                    */
  877.         toTouch->removedEntry = NULL;                                            /* make sure of back ptr                    */
  878.             
  879.         if ((toTouch->touchFlags & TouchedModified) == 0) {                         /* keep entry if        */
  880.             CMfree(cmDeleteListCell(&theToObject->touchedList, toTouch)); /* other things done*/
  881.             theFromValueHdr->touch = NULL;                                                                /* to touched value    */
  882.         }
  883.     }
  884.     
  885.     return (true);                                                                            /* return success                                    */
  886. }
  887.  
  888.  
  889. /*---------------------------------------------------------------------------------------*
  890.  | cmTouchDeletedValue - generate a touched list entry for a value that is being deleted |
  891.  *---------------------------------------------------------------------------------------*
  892.  
  893.  This routine defines a touched list entry for a value when an explicit CMDeleteValue() is
  894.  done.  A touched list entry for the object "owning" a value that is (to be) deleted is
  895.  defined.  The parameters specify the value header to be deleted (theValueHdr) and the
  896.  value's TOCObject (theObject).  The function returns true if everything went ok and false
  897.  otherwise.  This can only fail because of an allocation error creating a touched list
  898.  entry.
  899.  
  900.  Values are deleted individually and explicitly via CMDeleteValue(), or implicitly by
  901.  implicit deletion of the value's property.  An implicit property deletion comes about
  902.  when all the values of a property are explicitly deleted or moved out, or if the object
  903.  itself is deleted.  Implicit value deletions are handled cmImplicitDeleteValueTouch() 
  904.  specially as descriped there.
  905.  
  906.  For explicit deletions, touched list entry for a deleted value is always created or set
  907.  to "deleted value" in  the ORIGINAL object.  If the value has been moved to a different
  908.  object, then the "inserted" entry for the value (there must be one) has a back pointer to
  909.  the original touched list entry entry which must be flagged "removed".  That "removed"
  910.  entry is therefore changed to "deleted value" and the "insert" entry is removed.
  911. */
  912.  
  913. CMBoolean cmTouchDeletedValue(TOCValueHdrPtr theValueHdr, TOCObjectPtr theObject)
  914. {
  915.     ContainerPtr                 container = theValueHdr->container->updatingContainer;
  916.     TouchedListEntryPtr touch;
  917.     
  918.     /* Don't create touched list entries if we're not updating or we're processing a new    */
  919.     /* value header...                                                                                                                                        */
  920.     
  921.     if (!TouchIt(container, theValueHdr->container))
  922.         return (true);
  923.         
  924.     /* If we are deleting last value so that property will be deleted, record this as            */
  925.     /* delete property, this is because the property may be freed, so we will not know         */
  926.     /* the property id if we record it as delete value                                                                        */
  927.     
  928.     if (theValueHdr->theProperty->valueHdrList.nbrOfCells == 1)
  929.         return cmTouchDeletedProperty(theValueHdr->theProperty, theObject);
  930.             
  931.     /* If the value has never been touched, create a "deleted value" touched list entry     */
  932.     /* for it...                                                                                                                                                    */
  933.     
  934.     touch = theValueHdr->touch;                                                    /* not NULL if previously touched    */
  935.     
  936.     if (touch == NULL) {                                                                /* if this is the first touch...    */
  937.         touch = createTouchedListEntry(theValueHdr);            /* ...create "deleted value" entry*/
  938.         if (touch == NULL) return (false);
  939.     }
  940.     
  941.     /* At this point the value has a corresponding touched list entry. If it is the first    */
  942.     /* touch, or touched but not moved, or moved, but it's in the original object (i.e.,    */
  943.     /* there's no "removed" touched list entry), then change the existing touched list        */
  944.     /* entry to "deleted value".  Then we're done.                                                                                */
  945.     
  946.     if ((touch->touchFlags & TouchedInserted) == 0 || touch->removedEntry == NULL) {
  947.         touch->touchFlags = TouchedDeletedValue;                    /* changed to "deleted value"            */
  948.         return (true);                                                                        /* exit successfully                            */
  949.     }
  950.     
  951.     /* If the value has been moved to a different object, the back pointer in the                 */
  952.     /* corresponding touched list ("inserted") entry points to a "removed" entry in the        */
  953.     /* original object.  Change the "removed" entry to "deleted value" and delete the            */
  954.     /* "inserted" entry where ever the value happens to be at this moment.                                */
  955.     
  956.     touch->removedEntry->touchFlags = TouchedDeletedValue;     /* "removed"=>"deleted value"*/
  957.     CMfree(cmDeleteListCell(&theObject->touchedList, touch));/* delete "inserted" entry        */
  958.     
  959.     return (true);                                                                            /* return success                                    */
  960. }
  961.  
  962.  
  963. /*----------------------------------------------------------------------------------------*
  964.  | cmImplicitDeleteValueTouch - delete touched list entry for an implicitly deleted value |
  965.  *----------------------------------------------------------------------------------------*
  966.  
  967.  This routine is called whenever a value is IMPLICITLY deleted because an object or a
  968.  property is deleted.  In that case, all the values for the object, or one of the object's
  969.  property's are deleted.  We also must delete the the corresponding touched list entry
  970.  if there is one for those values since there isn't going to be any value there.  However,
  971.  if a value was moved in from another object, then the "removed" entry in the original
  972.  value is flagged as "deleted value".
  973.  
  974.  Implicit deletion of values because their property is going away is essentially a subset
  975.  of the explicit deletion case handled by cmTouchDeletedValue().  The difference between
  976.  deleting an individual value, and deleting a value when its property is going to be
  977.  deleted, is that a "deleted value" entry is created in the individual delete case, while
  978.  the entry is always deleted in the property value delete case.  What both have in common,
  979.  however, is that "removed" entries in other objects for values that were moved in to the
  980.  property being deleted are set to "deleted value".
  981.  
  982.  The current callers to this routine are (both in  TOCEnts.c  ):
  983.  
  984.  cmDeleteProperty()        Called when a CMDeleteObjectProperty() is done.  A single property
  985.                                             is deleted.  Hence all the values for that property are implicitly
  986.                                             deleted.
  987.  
  988.  deleteProperties()        Internal routine called when an object is deleted.  There, all the
  989.                                             properties of the object are to be deleted. This is action routine
  990.                                             for cmDelete1Object().  cmDelete1Object() is called whenever all the
  991.                                             properties are deleted by deleting values or deleting properties. 
  992.                                             In those cases however, values would have been singly deleted and
  993.                                             cmTouchDeletedValue() would be handling those.  Only when the
  994.                                             deletion is a byproduct does this routine get called.
  995. */
  996.  
  997. void cmImplicitDeleteValueTouch(TOCValueHdrPtr theValueHdr, TOCObjectPtr theObject)
  998. {
  999.     ContainerPtr                 container;
  1000.     TouchedListEntryPtr touch = theValueHdr->touch;
  1001.  
  1002.     if (touch != NULL) {                                                                /* if touched value...                        */
  1003.         if (touch->touchFlags & TouchedInserted)                     /* ...if "inserted"...                        */
  1004.             if (touch->removedEntry)                                                 /* ...and has "removed" entry            */
  1005.                 touch->removedEntry->touchFlags = TouchedDeletedValue;/* make a "deleted value" */
  1006.         
  1007.         container = theObject->container;                                                 /* needed for CMfree()            */
  1008.         CMfree(cmDeleteListCell(&theObject->touchedList, touch));/* delete touched entry        */
  1009.         theValueHdr->touch = NULL;                                                             /* fix hdr accordingly            */
  1010.     }
  1011. }
  1012.  
  1013.  
  1014. /*-------------------------------------------------------------------------------*
  1015.  | cmTouchDeletedProperty - generate a touched list entry for a deleted property |
  1016.  *-------------------------------------------------------------------------------*
  1017.  
  1018.  This routine defines a touched list entry for a property when the property is explicitly
  1019.  deleted by CMDeleteObjectProperty().  A touched list entry for the object "owning" the
  1020.  property that has been deleted is defined. The parameters specify the property of the
  1021.  object to be deleted (theProperty) and the property's TOCObject (theObject). The function
  1022.  returns true if everything went ok and false otherwise.  This can only fail because of an
  1023.  allocation error creating a touched list entry.
  1024.  
  1025.  Note, this routine is slightly different from touches on values in that no value is
  1026.  involved explicitly in the property delete.  They are, however, implicitly involved to 
  1027.  the degree that all values are deleted from the property (and the value headers put on
  1028.  the deleted values list).  The process of value deletion deletes all the touched list
  1029.  entries for those values in the property's object.  Any values moved into this object
  1030.  from other objects also have a "removed" touched list entry in the original object.
  1031.  Deleting such values for the property has exactly the same effect as CMDeleteValue(),
  1032.  i.e., the "removed" entry is changed to a "deleted value" entry.  See
  1033.  cmTouchDeletedValue() for further details.
  1034.  
  1035.  Note, that values previously moved OUT of the object containing the property being deleted 
  1036.  have "removed" entries of their own in this object.  Thus, there still could be "dangling"
  1037.  "removed" entries on this object's touched list; dangling, because no value header of the
  1038.  property being deleted points to it -- the value header has been moved out!  These must
  1039.  remain in the normal course of move handling.  However, see what happens to these things
  1040.  when the entire object is deleted!  Read cmTouchDeletedObject() for details.
  1041.  
  1042.  Because a deleted property must create a touched list entry, we must do it here, because
  1043.  the standard touched list creation routine (createTouchedListEntry()) deals only with
  1044.  values (headers).
  1045. */
  1046.  
  1047. CMBoolean cmTouchDeletedProperty(TOCPropertyPtr theProperty, TOCObjectPtr theObject)
  1048. {
  1049.     ContainerPtr                 container = theObject->container->updatingContainer;
  1050.     TouchedListEntryPtr touch;
  1051.     TOCValueHdrPtr            theValueHdr;
  1052.     
  1053.     /* Don't create touched list entries if we're not updating or we're processing a new    */
  1054.     /* object...                                                                                                                                                    */
  1055.     
  1056.     if (!TouchIt(container, theObject->container))
  1057.         return (true);
  1058.  
  1059.     /* Create the "deleted property" touched list entry...                                                                */
  1060.     
  1061.     if ((touch = (TouchedListEntryPtr)CMmalloc(sizeof(TouchedListEntry))) == NULL) {
  1062.         Container_Disable(container);
  1063.         ERROR1(CM_err_NoTouchedEntry, CONTAINERNAME);
  1064.         return (false);
  1065.     }
  1066.     
  1067.     cmNullListLinks(touch);                                                            /* init entry fields...                        */
  1068.     touch->theValueHdr     = NULL;                        
  1069.     touch->objectID         = theObject->objectID;
  1070.     touch->propertyID     = theProperty->propertyID;
  1071.     touch->typeID             = 0;
  1072.  
  1073.     /* When a property is deleted, values are also deleted. If all values are from a             */
  1074.     /* container that will later be merged, then we don't need to generate the delete            */
  1075.     /* update instruction later. So we look through this to see if there is at least one    */
  1076.     /* value from a container that is not a merge candidate                                                                */
  1077.     theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  1078.     while (theValueHdr) {
  1079.         if (!theValueHdr->container->updateMergeCandidate) {
  1080.             touch->typeID = theValueHdr->typeID;
  1081.             break;
  1082.         }
  1083.         theValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
  1084.     }
  1085.  
  1086.     touch->touchFlags        = TouchedDeletedProperty;                /* "deleted property" entry                */
  1087.     touch->removedEntry = NULL;
  1088.     
  1089.     cmAddObjToTouchedChain(container, theObject);                /* put obj. is on touched chain        */
  1090.     
  1091.     cmAppendListCell(&theObject->touchedList, touch);        /* add touch to the object                 */
  1092.  
  1093.     return (true);                                                                            /* return success                                    */
  1094. }
  1095.  
  1096.  
  1097. /*---------------------------------------------------------------------------*
  1098.  | cmTouchDeletedObject - generate a touched list entry for a deleted object |
  1099.  *---------------------------------------------------------------------------*
  1100.  
  1101.  This routine is called whenever an object is deleted. The object is placed on the touched
  1102.  chain if it is not already there.  The object's touched chain, if any, is deleted EXCEPT
  1103.  for "removed" entries for values previously moved out of this object.  
  1104.  
  1105.  The "removed" entries are kept because there are references to these entries from 
  1106.  "inserted" entries from the other objects (touched list) to where the values were moved.
  1107.  If a moved value is itself deleted after the object it came from is deleted, then the back
  1108.  pointer in its "inserted" entry must be kept valid so that the "removed" entry it points
  1109.  to can be changed to a "deleted value".  Although this "deleted value" is on a touched
  1110.  list of a deleted object, it won't cause harm, since the final walks of the touched chain
  1111.  know to expect this.
  1112.  
  1113.  Deleted objects are moved to the deleted objects list and flagged as deleted.  This is
  1114.  done to protect against the user reusing the refNum after the delete.  Thus an object on
  1115.  the touched chain, but flagged as deleted, is enough to indicated to close-time
  1116.  processing what is going on.  The remaining touched list "goes along for the ride".
  1117.  
  1118.  This routine MUST be called AFTER all objects and properties for the object have been
  1119.  deleted.  By that time all touched list entries for values will have been processed
  1120.  (using cmImplicitDeleteValueTouch()).   The reason it must be after is that if it were
  1121.  before, we could have touched value headers pointing to touched list entries we free up
  1122.  here.  Not too cool!  cmImplicitDeleteValueTouch() would have a hard time dealing with
  1123.  that.
  1124. */
  1125.  
  1126. void cmTouchDeletedObject(TOCObjectPtr theObject)
  1127. {
  1128.     ContainerPtr                 container = theObject->container->updatingContainer;
  1129.     TouchedListEntryPtr touch, nextTouch;
  1130.     
  1131.     /* Don't create touched list entries if we're not updating or we're processing a new    */
  1132.     /* object...                                                                                                                                                    */
  1133.     
  1134.     if (!TouchIt(container, theObject->container))
  1135.         return;
  1136.  
  1137.     /* Look at the remaining touched list entries for the deleted object (if any).  By         */
  1138.     /* point there should only be "removed" entries for values previously moved out of         */
  1139.     /* this object.  These must remain so that if a moved value is itself deleted, its        */
  1140.     /* "inserted" entry back pointer has a valid "removed" entry to point at!  Everything    */
  1141.     /* else that might be left on the touched list is freed.  There should be anything,        */
  1142.     /* but what the hell.  Continuing in this "paranoid" mode, if we see any "inserted"        */
  1143.     /* entries, we use thier back pointers to change their corresponding "removed" entries*/
  1144.     /* to "deleted value".                                                                                                                                */
  1145.     
  1146.     touch = (TouchedListEntryPtr)cmGetListHead(&theObject->touchedList);
  1147.     
  1148.     while (touch) {                                                                                                /* process touched list    */
  1149.         nextTouch = (TouchedListEntryPtr)cmGetNextListCell(touch);    /* get next touch entry    */
  1150.         
  1151.         if (touch->touchFlags & TouchedInserted)                                         /* ...if "inserted"...    */
  1152.             if (touch->removedEntry)                                                                     /* ...+ "removed" entry    */
  1153.                 touch->removedEntry->touchFlags = TouchedDeletedValue;    /* make "deleted value"    */
  1154.         
  1155.         if ((touch->touchFlags & TouchedRemoved) == 0) {                            /* if not "removed"...    */
  1156.             if (touch->theValueHdr)
  1157.                 touch->theValueHdr->touch = NULL;
  1158.             CMfree(cmDeleteListCell(&theObject->touchedList, touch));    /* delete touched entry    */
  1159.         }        
  1160.         touch = nextTouch;                                                                                    /* process next entry        */
  1161.     } /* while */
  1162.     
  1163.     /* Make sure object is on the touched chain...                                                                                */
  1164.     
  1165.     cmAddObjToTouchedChain(container, theObject);
  1166. }
  1167.  
  1168.  
  1169. /*---------------------------------------------------------------------------------*
  1170.  | generateDataEdits - generate insert/delete (data editing) updating instructions |
  1171.  *---------------------------------------------------------------------------------*
  1172.  
  1173.  This routine is called only by genValueUpdateInstructions() to generate data edits, i.e.
  1174.  data insert and delete sequences, for a value.  The value header and the owning object's
  1175.  special "updates" property value refNum are passed.  
  1176.  
  1177.  The value refNum is where the updating instructions are written.  The value header is
  1178.  used to analyze the value segments to see what has to be done.  The fact that this 
  1179.  routine is called at all means that this value has been touched because it was written 
  1180.  to or had some of its data deleted.  We also know that the value is not immediate since
  1181.  they are handled separately using a distinct touch (flag).  Further, by this point, the
  1182.  value header ptr is not NULL and has at least one segment.  All of these checks were
  1183.  done.
  1184.  
  1185.  In order to understand the algorithm used here to determine data inserts and deletes, the
  1186.  "logical offset" and "logical size" for a value must be described and what is expected in
  1187.  the segment arrangements.  Assume the following example value header and sequence of
  1188.  value segments:
  1189.  
  1190.  *-------------*   X                                                     Y
  1191.  |  Value Hdr  |   *-------------*   *-------------*   *-------------*   *-------------*
  1192.  |             |-->|  Value Seg. |-->|  Value Seg. |-->|  Value Seg. |-->|  Value Seg. |
  1193.  |- - - - - - -|   |- - - - - - -|   |- - - - - - -|   |- - - - - - -|   |- - - - - - -|
  1194.  |             |   | "log. off." |   |    (new)    |   |    (new)    |   | "log. off." |
  1195.  | "log. size" |   *-------------*   *-------------*   *-------------*   *-------------*
  1196.  *-------------*   |<---- l ---->|
  1197.  
  1198.  The "logical size" is the original total size of the data as it was read in at open time.
  1199.  It remains constant in the value header.  The "logical offset" is the original start of
  1200.  each value segment when the TOC was loaded at open time.  This is the relative offset of
  1201.  the start of each value segment, viewing each segment as the user would, i.e., as
  1202.  contiguous data.  This offset has nothing to do with the actual data offset in the
  1203.  container.
  1204.  
  1205.  As each segment is created, it is "tagged" to its "owning" container.  This tag takes the
  1206.  form of a pointer to the container (control block) that owns it.  "New" segments are 
  1207.  therfore created after open time and will always belong to the updating container.  "Old"
  1208.  segments are any segments not belonging to the updating container.
  1209.  
  1210.  Therfore, a data insert can always be determined simply by seeing if the segment is a
  1211.  "new" segment.  In the above example, two "new" segments appear between two "old"
  1212.  segments.  
  1213.  
  1214.  Of course, "new" segments" never have a valid logical offset given to them.  The logical
  1215.  offsets are only set at open time in the "old" segments.  They supply the information
  1216.  needed to determine a delete and also where new segments are inserted.
  1217.  
  1218.  When data is deleted from an old segment, its logical offset is "corrected" as necessary.
  1219.  For example, if a segment started at offset X, and the n left-most bytes are deleted from
  1220.  that segment, then its corrected logical offset is X+n.  Corrections are done for all
  1221.  the delete possibilites on old segments as appropriate.
  1222.  
  1223.  Thus, knowing that ORIGINALLY the logical offsets in each old segment were contiguous
  1224.  (i.e., the logical offset for a segment plus that segments length exactly equals the
  1225.  logical offset of the next old segment), a deletion is detected when the starting offset 
  1226.  is different than what's expected for it.  The "expected" logical offset of the next old
  1227.  segment is always the offset of current segment plus the current segment's length.  The
  1228.  original contiguous set of segments always has the "expected" offsets. 
  1229.  
  1230.  Deletions can only produce a corrected logical offset that is greater than what is 
  1231.  expected.  The difference of the corrected logical offset and what is expected is the
  1232.  number of bytes deleted.  The starting point for the deleted is just the expected logical
  1233.  offset.
  1234.  
  1235.  Note, for insertions, the insertion point is also the expected logical offset.  If the
  1236.  next segment after an old segment is a new segment, then it must be at the expected 
  1237.  offset.  This is true even if an old segment had to be split to do the insertion.  The
  1238.  logical offsets in the split old segments are appropriately corrected to be "contiguous"
  1239.  (from a logical offset point of view).
  1240.  
  1241.  All through the above discussion the point was made that a deletion is determined using
  1242.  the expected logical offset and the logical offset of the "next old" segment.  The
  1243.  emphasis is on the "next old", because it doesn't matter how many new segments may be 
  1244.  inserted between the two old segments.  This is illustrated above.  Thus both a delete
  1245.  and insert can be detected and produced during analysis.  Indeed, overwrites are
  1246.  produced when updating as deletes followed by inserts.
  1247.  
  1248.  So, in the above example, the first old segment starts at offset X (in reality, the first
  1249.  segment will always be 0, but generalize this to an arbitrary sequence of segments that
  1250.  may not be first).  It's length is l.  The expected logical offset for the next old
  1251.  segment is X+l.  The next old segment starts at logical offset Y.  Thus if Y > (X+l) then
  1252.  (X+l)-Y bytes have been deleted.  Also, two new segments are inserted at X+l.
  1253.  
  1254.  The algorithm below always assumes we start with an old segment and looks for the next
  1255.  old segment, keeping track of new segments in between.  The expected offset is maintained.
  1256.  The deletes and/or inserts are generated whenever we don't get what's expected or we
  1257.  encountered new segments.  That's basically all there is to it!
  1258.  
  1259.  One final point.  Nothing was mentioned above about how the "logical size" is used.  
  1260.  Essentially, it just modifies this algorithm by always pretending there is a final old
  1261.  segment whose logical offset is the logical size.  This is needed when checking for
  1262.  deletions of the last n bytes of the data.
  1263.  
  1264.  Finally, the insert/delete updating instructions generated have the following formats:
  1265.  
  1266.                                             +---+---+---+---+---+---+---+---+---+
  1267.          DeleteData       |   |starting offset|    amount     |
  1268.                                             +---+---+---+---+---+---+---+---+---+
  1269.                                     
  1270.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+
  1271.          InsertData1      |   | insert point  |  data offset  |  data length  |
  1272.              (N = 1)                 +---+---+---+---+---+---+---+---+---+---+---+---+---+
  1273.  
  1274.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  1275.          InsertDataN      |   | insert point  |       N       | data offset1  | data length1  |
  1276.              (N > 1)                +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  1277.                                                                                                                                               - - -
  1278.                                                           +---+---+---+---+---+---+---+---+
  1279.                                                                                                                     | data offsetN  | data lengthN  |
  1280.                                                                                                                     +---+---+---+---+---+---+---+---+
  1281.  
  1282.  There are two data insert formats; one when only one segment is to be inserted, and one
  1283.  when N > 1.
  1284. */
  1285.  
  1286. static void CM_NEAR generateDataEdits(TOCValueHdrPtr theValueHdr, void *ioBuffer)
  1287. {
  1288.     ContainerPtr     container = theValueHdr->container->updatingContainer;
  1289.     TOCValuePtr        theValue, theNewValue;
  1290.     CM_ULONG          expected, nextOldLogicalOffset, nbrOfInsertedValues; 
  1291.     long                    adjustment, deleteSize, insertSize;            
  1292.     
  1293.     /* To make this code a little easire to follow, the following macros are defined...        */
  1294.     
  1295.     #define NewValue(v)      ((v)->container == container)    /* check for an "new" value seg.    */
  1296.     #define OldValue(v)     ((v)->container != container)    /* check for an "old" value seg.    */
  1297.     
  1298.     #define LogicalOffset(v)    ((v)->logicalOffset)            /* logical offset of an old seg.    */
  1299.     #define LogicalSize(vh)        ((vh)->logicalSize)                /* logical size                                     */
  1300.     
  1301.     /* Process all the segments.  The variable "expected" is maintained as the expected     */
  1302.     /* offset to the next segment.                                                                                                                */
  1303.     
  1304.     expected = 0;                                                                                /* 0 is initial expected offset        */
  1305.     adjustment = 0;                                                                            /* no "adjustment" yet                        */
  1306.     
  1307.     for (theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);;) {
  1308.         
  1309.         /* Skip contiguous old segments, i.e., old segments that have the expected offsets.    */
  1310.         
  1311.         while (theValue != NULL        &&                                            /* skip while there are segments    */
  1312.                      OldValue(theValue) &&                                            /* ...and its "old"                                */
  1313.                      LogicalOffset(theValue) == expected) {            /* ...and is contiguous...                */
  1314.             expected = LogicalOffset(theValue) + theValue->value.notImm.valueLen;
  1315.             theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  1316.         }
  1317.         
  1318.         /* A new segment or an old segment with a logical offset that was not expected has    */
  1319.         /* been seen (or there are no more segments).  Remember this segment assuming it's     */
  1320.         /* new segment and skip over the new segments if it indeed is a neew segment.  A        */
  1321.         /* count of the number of new segments is done to indicate later that we do have an    */
  1322.         /* insertion and how many segments.  This will also iindicate which insertion                */
  1323.         /* control code to generate (InsertData1 or InsertDataN).                                                        */
  1324.         
  1325.         theNewValue = theValue;                                                        /* remember where the segment is    */
  1326.         nbrOfInsertedValues = 0;                                                    /* assume it's not a new segment    */
  1327.         insertSize = 0;                                                                        /* accumulate size of this insert    */
  1328.         
  1329.         while (theValue != NULL && NewValue(theValue)) {    /* count nbr of new inserted segs    */
  1330.             insertSize += theValue->value.notImm.valueLen;    /* ...and size of this insertion    */
  1331.             ++nbrOfInsertedValues;                                        
  1332.             theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  1333.         }
  1334.         
  1335.         /* The current segment is always an old segment here (assuming "old" if there is no    */
  1336.         /* more segments).  This old segment is either the one immediately following a set     */
  1337.         /* of one or more newly inserted segments, or it's just then next old segment that    */
  1338.         /* doesn't have the expected logical offset.  Either way, we process deletions here.*/ 
  1339.         
  1340.         if (theValue == NULL || OldValue(theValue)) {            /* have next old segment...                */
  1341.             nextOldLogicalOffset = (theValue != NULL) ? LogicalOffset(theValue) :
  1342.                                                                                                     LogicalSize(theValueHdr);
  1343.             if (expected < nextOldLogicalOffset) {                    /* generate deletion if required    */
  1344.                 deleteSize = nextOldLogicalOffset - expected;
  1345.                 PUT1(DeleteData                    , ioBuffer);                     /* <DeleteData>                                        */
  1346.                 PUT4(expected+adjustment, ioBuffer);                    /*                          offset                            */
  1347.                 PUT4(deleteSize                    , ioBuffer);                    /*                                   amount                */
  1348.             } else
  1349.                 deleteSize = 0;
  1350.         } /* deletions */
  1351.         
  1352.         /* If new segments were encountered, generate the insertion instructions...                    */
  1353.         
  1354.         if (nbrOfInsertedValues > 0) {                                        /* if N inserted segments (N > 0)    */
  1355.             if (nbrOfInsertedValues == 1) {                                    /* N = 1...                                                */
  1356.                 PUT1(InsertData1                , ioBuffer);                    /* <InsertData1>                                    */
  1357.                 PUT4(expected+adjustment, ioBuffer);                    /*              insertPt                    */
  1358.             } else {                                                                                /* N > 1...                                                */
  1359.                 PUT1(InsertDataN                , ioBuffer);                    /* <InsertDataN>                                    */
  1360.                 PUT4(expected+adjustment, ioBuffer);                    /*              insertPt                    */
  1361.                 PUT4(nbrOfInsertedValues, ioBuffer);                    /*                       N                */
  1362.             }
  1363.             
  1364.             while (nbrOfInsertedValues--) {                                    /* generate N offset/length pairs    */
  1365.                 PUT4(theNewValue->value.notImm.value   , ioBuffer);
  1366.                 PUT4(theNewValue->value.notImm.valueLen, ioBuffer);
  1367.                 theNewValue = (TOCValuePtr)cmGetNextListCell(theNewValue);
  1368.             }
  1369.         } /* insertions */
  1370.         
  1371.         /* Exit if there are no more segments.  If there are, reset the expected logical         */
  1372.         /* offset to the (corrected) value in the current segment.  Processing continues         */
  1373.         /* using this segment.  Thus the end of one interation around this loop overlaps         */
  1374.         /* next, with the "end" old segment of the previous interation being the "start"        */
  1375.         /* segment of the next iteration.                                                                                                        */
  1376.         
  1377.         if (theValue == NULL) break;                                            /* done if no more segments                */
  1378.         expected = LogicalOffset(theValue);                                /* reset expected logical offset    */
  1379.         adjustment += (insertSize - deleteSize);                    /* fix "adjustment" to expected        */
  1380.     } /* for */
  1381. }
  1382.  
  1383.  
  1384. /*-------------------------------------------------------------------*
  1385.  | genValueUpdateInstructions - generate value updating instructions |
  1386.  *-------------------------------------------------------------------*
  1387.  
  1388.  This routine is called only by generateValueUpdates() to generate the specific value
  1389.  updating instruction sequences needed to bring a value "up-to-date".  The touched list
  1390.  entry for the value and the owning object's special "updates" property value refNum are
  1391.  passed.
  1392.  
  1393.  The value refNum is where the updating instructions are written.  The touch list entry
  1394.  pointer indicates what has been done to the value and its original "OPT" address.  The
  1395.  entry also points to the touched value header (except for "removed" values).  The value
  1396.  header's flags, in turn, can add additional information about what's been done to the
  1397.  value.  Specifically, whether the value's been edited, set-infoed, immediate, and so on.
  1398.  
  1399.  By the time generateValueUpdates() calls this routine all the updating instructions 
  1400.  sequences needed to address the value in question have been output.  A "<new prop> P T"
  1401.  sequence when the property changes, or a "<new type> T" sequence when a new value is
  1402.  to be processed but for the "current" same property.  The object is never needed since
  1403.  these sequences are attached to the object in its special "updates" property.
  1404.  
  1405.  After generateValueUpdates() generates the "<new prop> P T" or "<new type> T", it calls
  1406.  this routine to generate "<v> [params...]", where <v> is a control code (byte) and
  1407.  [params...] is one or more parameters as a function of that control code.  The following
  1408.  diagrams show the exact layout of the sequences generated for each value control code:
  1409.  
  1410.                                             +---+
  1411.          RemovedValue     |   |
  1412.                                             +---+
  1413.                                     
  1414.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+
  1415.          InsertedValue    |   |  original O   |  original P   |  original T   |
  1416.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+
  1417.                                                                 
  1418.                                             +---+---+---+---+---+---+---+---+---+
  1419.          SetInfoedValue   |   |     new T     |  geneneration |
  1420.                                             +---+---+---+---+---+---+---+---+---+
  1421.                                     
  1422.                                             +---+---+---+---+---+---+---+---+---+
  1423.          DeleteData       |   |starting offset|    amount     |
  1424.                                             +---+---+---+---+---+---+---+---+---+
  1425.                                     
  1426.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+
  1427.          InsertData1      |   | insert point  |  data offset  |  data length  |
  1428.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+
  1429.                                     
  1430.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  1431.          InsertDataN      |   | insert point  |       N       | data offset1  | data length1  |
  1432.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  1433.                                                                                                                                               - - -
  1434.                                                           +---+---+---+---+---+---+---+---+
  1435.                                                                                                                     | data offsetN  | data lengthN  |
  1436.                                                                                                                     +---+---+---+---+---+---+---+---+
  1437.  
  1438.                                             +---+---+---+---+---+---+
  1439.          ReplaceImmediate |   | l | new imm value |
  1440.           (length=l<=4)   +---+---+---+---+---+---+
  1441.                                     
  1442.                                             +---+---+---+---+---+---+---+       +---+---+---+---+
  1443.          ReplaceBaseType  |   |n bases| base type #1  | - - - | base type #n  |
  1444.                                             +---+---+---+---+---+---+---+       +---+---+---+---+
  1445.          
  1446.                                             +---+
  1447.          DeleteValue      |   |
  1448.                                             +---+
  1449.  
  1450.  The way to view a complete value update sequence, i.e., "<new prop> P T <v> [params...]"
  1451.  or "<new type> T <v> [params...]" is that the "<v> [params...]" act upon their receiver.
  1452.  It's sort of like a postfix notation.
  1453.  
  1454.  Note also that more that one sequence may be generated for a specific value.  Thus the
  1455.  [params...] definition is recursive and itself can have "<v> [params...]" sequences.
  1456.  
  1457.  The following code tests each of the value touch flags to generate each flag's associated
  1458.  sequence as described above.  The tests follow one after another.  Some return if
  1459.  satisfied, other fall through to the next test. THE TESTING ORDER IS NOT ARBITRARY (except
  1460.  for, perhaps, the ones that don't fall through).  Here are the tests in the indicated
  1461.  order and the reason(s) they are in that order.
  1462.     
  1463.  1. "removed"                            Value removed from this object.  Nothing else can be done to it.
  1464.                                                      Done first to get it out of the way.
  1465.     
  1466.  2. "deleted value"                Value deleted from this object. Nothing else can be done to it.
  1467.                                                     Also done second to get it out of the way too.  It could have
  1468.                                                     been done first.
  1469.     
  1470.  3. "inserted"                        Data moved from someplace else. This must be done before anything
  1471.                                                      further is done to the value. It has to be moved to this object
  1472.                                                     from its original address since this set of update instructions
  1473.                                                     is attached to the destination object and property (where the
  1474.                                                     value was moved to).  The destination property for the object
  1475.                                                     need NOT exist prior to the move at open-time.  The move brings
  1476.                                                     it into existance.  Thus "inserted" has to be first so that there
  1477.                                                     is a value to operate on by the other updates.
  1478.  
  1479.  All the following tests deal with a value (header) that remains after updating.  These
  1480.  tests fall through to one another.  Most are mutually exclusive, but the order is more or
  1481.  less logically chosen.
  1482.  
  1483.  4. "base type changed"        Replace base type array of IDs.  Base types are basically
  1484.                                                      immediate data.  Immediates are processed early to get them out
  1485.                                                     of the way too.  Base types, however, cannot be set-infoed.  So
  1486.                                                     they are processed as the first value operation.
  1487.     
  1488.  5. "set-infoed"                    Set-infoing can be done in addition to all further sequences so
  1489.                                                      it should be done now.
  1490.     
  1491.  6. "immediate"                        Replace immediate with new value.  This is the last of the
  1492.                                                      immediate value possibilities.  A "few words" should be said
  1493.                                                     about what happens to immediate data if it needed to be converted
  1494.                                                     to non-immediate during editing...
  1495.                                                     
  1496.                                                     If a value has made it this far and is still an immediate, then
  1497.                                                     a "replace immediate" is the most efficient way to deal with it.
  1498.                                                     If, along the way, it was converted to a non-immediate, then 
  1499.                                                     the immediate data is written to the updating container as "new"
  1500.                                                     data. From our point of view here it looks as though the value
  1501.                                                     data was completely overwritten (i.e., deleted and new data
  1502.                                                     inserted).  We can't tell the difference, and the delete/insert
  1503.                                                     sequences, as done with non-immidate overwrites is generated.
  1504.                                                     There is no special case.
  1505.                                                     
  1506.  7. "edited"                            Data insert and delete are the last (and most complicated) 
  1507.                                                      operations that can be performed on the (non-immediate) data, so
  1508.                                                     they are placed last (so I can avoid figuring out how to code 
  1509.                                                     them as long as possible -- how's that for a reason?).
  1510. */
  1511.  
  1512. static void CM_NEAR genValueUpdateInstructions(TouchedListEntryPtr touch, void *ioBuffer)
  1513. {
  1514.     TOCValueHdrPtr theValueHdr;
  1515.     TOCValuePtr         theValue;
  1516.     CM_USHORT             touchFlags = touch->touchFlags;
  1517.     
  1518.     /* 1. "removed": <RemovedValue>                                                                                                                */
  1519.     
  1520.     if (touchFlags & TouchedRemoved) {                                        /* 1. "removed"                                    */
  1521.         PUT1(RemovedValue, ioBuffer);                                                /* <RemovedValue>                                */
  1522.         return;                                                                                            /* exit                                                    */
  1523.     }
  1524.  
  1525.     theValueHdr = touch->theValueHdr;                                            /* point at the touched valueHdr*/
  1526.  
  1527.     /* 2. "deleted value": <DeleteValue>                                                                                                    */
  1528.     
  1529.     if (touchFlags & TouchedDeletedValue) {                                /* 2. "deleted value"                        */
  1530.         PUT1(DeleteValue, ioBuffer);                                                /* <DeleteValue>                                */
  1531.         return;                                                                                            /* exit                                                    */
  1532.     }
  1533.         
  1534.     /* 3. "inserted": <InsertedValue>    O P T                                                                                                */
  1535.  
  1536.     if (touchFlags & TouchedInserted) {                                        /* 3. "inserted"                                */
  1537.         PUT1(InsertedValue        , ioBuffer);                                    /* <InsertedValue>                            */
  1538.         PUT4(touch->objectID    , ioBuffer);                                    /*                O                            */
  1539.         PUT4(touch->propertyID, ioBuffer);                                    /*                                     P                        */
  1540.         PUT4(touch->typeID        , ioBuffer);                                    /*                                        T                    */
  1541.     }
  1542.     
  1543.     /* The following are all the value data or info manipulations...                                            */
  1544.     
  1545.     theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList); /* and 1st value            */
  1546.  
  1547.     /* 4: "base type changed": <ReplaceBaseType> N baseType1 . . . baseTypeN                            */
  1548.     
  1549.     if (touchFlags & TouchedBaseType) {                                        /* 4: "base type changed"                */
  1550.         PUT1(ReplaceBaseType, ioBuffer);                                        /* <ReplaceBaseType>                        */
  1551.         
  1552.         PUT2(cmCountListCells(&theValueHdr->valueList), ioBuffer);/*           N                        */
  1553.         
  1554.         while (theValue != NULL) {                                                    /*                                 bt1 . . . btN*/
  1555.             PUT4(theValue->value.imm.ulongValue, ioBuffer);
  1556.             theValue = (TOCValuePtr)cmGetNextListCell(theValue); 
  1557.         }
  1558.     }
  1559.     
  1560.     /* 5: "set-infoed": <SetInfoedValue> T g                                                                                            */
  1561.  
  1562.     if (touchFlags & TouchedSetinfoed) {                                    /* 5: "set-infoed"                            */
  1563.         PUT1(SetInfoedValue                    , ioBuffer);                        /* <SetInfoedValue>                            */
  1564.         PUT4(theValueHdr->typeID        , ioBuffer);                        /*                                 T                        */
  1565.         PUT4(theValueHdr->generation, ioBuffer);                        /*                                    g                        */
  1566.     }
  1567.     
  1568.     /* 6: "immediate": <ReplaceImmediate> length value                                                                         */
  1569.     
  1570.     if (touchFlags & TouchedImmediate) {                                        /* 6: "immediate"                                */
  1571.         if (theValue->flags & kCMImmediate) {                                /* ...only if not converted!        */
  1572.             PUT1(ReplaceImmediate                                , ioBuffer);    /* <ReplaceImmediate>                        */
  1573.             PUT1(theValue->value.notImm.valueLen, ioBuffer);    /*                   length            */
  1574.             PUT4Direct(theValue->value.imm.ulongValue, ioBuffer);/*                                value    */
  1575.             return;
  1576.         }
  1577.     }
  1578.     
  1579.     /* 7: "edited": <DeleteData> offset amount                                                                                        */
  1580.     /*                            <InsertDataX> [N] insertPt offset length [...]                                                */
  1581.     
  1582.     if (touchFlags & TouchedEdited)                                              /* 7: "edited"                                    */
  1583.         generateDataEdits(theValueHdr, ioBuffer);                     /* generate separately...                */
  1584. }
  1585.  
  1586.  
  1587. /*------------------------------------------------------------------*
  1588.  | cmPastUpdateOnTouchList - make sure past update is on touch list |
  1589.  *------------------------------------------------------------------*
  1590.  
  1591.     When we are doing merging of updating containers, what we are doing is to take all
  1592.     the updates in the top container, and generate them again as updates in the
  1593.     merged containers. However these updates may not have been touched in the latest
  1594.     edit. To ensure they will not be omitted in the generate update process, we go
  1595.     through all the past updates and put them on the touch list so they will be dealt
  1596.     with during the generate update process.
  1597. */
  1598.     
  1599. void cmPastUpdateOnTouchList(ContainerPtr container)
  1600. {
  1601.     PastUpdate            *pastUpdate = cmGetListHead(&container->tmpList);
  1602.     void                         *toc = container->updatingContainer->toc;
  1603.     TOCObjectPtr        theObject;
  1604.         
  1605.     while (pastUpdate != NULL) {                                            /* ...loop through all past update     */
  1606.         theObject = cmFindObject(toc, pastUpdate->objectID);
  1607.         /* make sure object is on touched chain                                                                                            */
  1608.         if (theObject)
  1609.             cmAddObjToTouchedChain(container->updatingContainer, theObject);
  1610.         pastUpdate = (PastUpdate *)cmGetNextListCell(pastUpdate);
  1611.     } /* while */
  1612. }
  1613.  
  1614. /*---------------------------------------------------------------*
  1615.  | totalInsertionSize - sum of the insertion size in InsertDataN |
  1616.  *---------------------------------------------------------------*
  1617.  
  1618.     This routine calculate the total size in an InsertDataN PastInsertData
  1619.     structure.
  1620. */
  1621.  
  1622. static CM_ULONG CM_NEAR totalInsertionSize(const PastInsertData *pastUpdate)
  1623. {
  1624.     CM_ULONG                total = pastUpdate->size;
  1625.     CM_ULONG                n = pastUpdate->insertCount;
  1626.     OffsetSizePair    *offsetInfoPtr = (OffsetSizePair *)(++pastUpdate);
  1627.     
  1628.     while (--n) {    
  1629.         total += offsetInfoPtr->size;
  1630.         offsetInfoPtr++;
  1631.     }
  1632.     return total;
  1633. }
  1634.     
  1635. /*-------------------------------------------------------------*
  1636.  | putBackOldOffset - sum of the insertion size in InsertDataN |
  1637.  *-------------------------------------------------------------*
  1638.  
  1639.     Update instructions represents the changes that occurs in each container. When we
  1640.     open a chain of updating containers, the deepest containers has the value in their
  1641.     initial state and each value has a logical offset. The update within a container
  1642.     will change the values around. Before we apply the updates in the next container,
  1643.     the logical offset will be recalculated to reflect the state of the values at
  1644.     the time of the opening of the next container. The logical offsets are also
  1645.     used to generate any new update that occurs.
  1646.     
  1647.     For merge of updating containers, we need to get the logical offset before
  1648.     the top level updates have been applied. However their values has already
  1649.     been changed. Fortunately we know all the updates that occured in the top container.
  1650.     So we can reverse the process and recover the original logical offset and logical
  1651.     size of the values. Basically if there is a deleteion and add back the deleted
  1652.     size and if there is an insertion we subtract the insert size.
  1653.     
  1654.     However, there can be mulitple insert and delete updating instructions on a value, and
  1655.     we need to use all of them to recalcuate. So we will scan the past update list to
  1656.     get all the updates for the same value and use all of them. The other updates that
  1657.     we found will be removed from the list so that they would be processed for a second
  1658.     time.
  1659. */
  1660.  
  1661. static PastUpdate CM_NEAR *putBackOldOffset(ContainerPtr container,
  1662.                                                                                         TOCObjectPtr theObject, 
  1663.                                                                                         PastDeleteData *pastUpdate)
  1664. {
  1665.     TOCPropertyPtr                 theProperty;
  1666.     TOCValueHdrPtr                 theValueHdr;
  1667.     TOCValuePtr                         theValue;
  1668.     PastUpdate                        *lastDeleteUpdate, *nextUpdate;
  1669.     CM_ULONG                            orgOffset;
  1670.  
  1671.     theProperty = cmGetObjectProperty(theObject, pastUpdate->updateHeader.propertyID);
  1672.     if (theProperty) {                                                    
  1673.         theValueHdr = (TOCValueHdrPtr)cmGetPropertyType(theProperty, pastUpdate->updateHeader.typeID);
  1674.         if (theValueHdr) {    
  1675.             /* first we can the past update list to find all updates for this value header        */
  1676.             nextUpdate = (PastUpdate *)cmGetNextListCell(pastUpdate);
  1677.             lastDeleteUpdate = (PastUpdate *)pastUpdate;
  1678.             while (nextUpdate) {
  1679.                 if ((nextUpdate->objectID == pastUpdate->updateHeader.objectID) &&
  1680.                         (nextUpdate->propertyID == pastUpdate->updateHeader.propertyID) &&
  1681.                         (nextUpdate->typeID == pastUpdate->updateHeader.typeID)) {
  1682.                     if ((nextUpdate->ctlByte == DeleteData) || (nextUpdate->ctlByte == InsertData1) || (nextUpdate->ctlByte == InsertDataN))
  1683.                         lastDeleteUpdate = nextUpdate;
  1684.                     else
  1685.                         break;
  1686.                     nextUpdate = (PastUpdate *)cmGetNextListCell(nextUpdate);
  1687.                 }
  1688.                 else
  1689.                         break;
  1690.             }
  1691.             theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  1692.             while (theValue) {
  1693.                 if (theValue->container->depth) {        /* we only need to worry about old value        */
  1694.                     /* not all insertion and deletion will affect a value, an insertion or                */
  1695.                     /* occuring in an offset beyond this value will have no effect                                */
  1696.                     /* However, we need to add up all the insertion and deletion that occurs at        */
  1697.                     /* an offset smaller than this value.                                                                                 */
  1698.                     /* We first get the original logical offset into a local variable because            */
  1699.                     /* the comparision will be against this and we don't want to change it                */
  1700.                     orgOffset = theValue->logicalOffset;
  1701.                     nextUpdate = ((PastUpdate *)pastUpdate);
  1702.                     while (true) {
  1703.                         if (nextUpdate->ctlByte == DeleteData) {
  1704.                             if (orgOffset >= ((PastDeleteData *)nextUpdate)->offset)
  1705.                                 theValue->logicalOffset += ((PastDeleteData *)nextUpdate)->size;
  1706.                         }
  1707.                         else if (nextUpdate->ctlByte == InsertData1) {
  1708.                             if (orgOffset > ((PastInsertData *)nextUpdate)->insertPoint)
  1709.                                 theValue->logicalOffset -= ((PastInsertData *)nextUpdate)->size;
  1710.                         }
  1711.                         else if (nextUpdate->ctlByte == InsertDataN) {
  1712.                             if (orgOffset > ((PastInsertData *)nextUpdate)->insertPoint)
  1713.                                 theValue->logicalOffset -= totalInsertionSize((PastInsertData *)nextUpdate);
  1714.                         }
  1715.                         if (nextUpdate == lastDeleteUpdate)
  1716.                             break;
  1717.                         nextUpdate = (PastUpdate *)cmGetNextListCell(nextUpdate);
  1718.                     }
  1719.                 }
  1720.                 theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  1721.             }    /* loop through all the value segments */
  1722.             
  1723.             /* Now we recalcuate the orignal logical size of the value. All insertions and        */
  1724.             /* deletion will affect this value                                                                                                */
  1725.             while (true) {
  1726.                 if (lastDeleteUpdate->ctlByte == DeleteData) {
  1727.                     theValueHdr->logicalSize += ((PastDeleteData *)lastDeleteUpdate)->size;
  1728.                 }
  1729.                 else if (lastDeleteUpdate->ctlByte == InsertData1) {
  1730.                     theValueHdr->logicalSize -= ((PastInsertData *)lastDeleteUpdate)->size;
  1731.                 }
  1732.                 else if (nextUpdate->ctlByte == InsertDataN) {
  1733.                     theValueHdr->logicalSize -= totalInsertionSize((PastInsertData *)lastDeleteUpdate);
  1734.                 }
  1735.                 if ((PastDeleteData *)lastDeleteUpdate != pastUpdate) {
  1736.                     /* any extra past delete that has been processed will be removed                            */
  1737.                     nextUpdate = (PastUpdate *)cmGetPrevListCell(lastDeleteUpdate);
  1738.                     CMfree(cmDeleteListCell(&container->tmpList, lastDeleteUpdate));
  1739.                     lastDeleteUpdate = nextUpdate;
  1740.                 }
  1741.                 else
  1742.                     break;
  1743.             } /* loop through all the past update for this value header                                                */
  1744.         }
  1745.     }
  1746.     /* we may have change the list, so the next item will be different, that is why we        */
  1747.     /* pass back the next item in the past update list.                                                                        */
  1748.     return (PastUpdate *)cmGetNextListCell(pastUpdate);
  1749. }
  1750.     
  1751. /*------------------------------------------------------------*
  1752.  | cmClosePastDataGap - adjust the offset in the past history |
  1753.  *------------------------------------------------------------*
  1754.  
  1755.     When we return free space from a embedded container to the parent container, the
  1756.     offset of the values will be changed. This affects not only the offset on the TOC.
  1757.     It also affects the offset in the update objects. So we need to be able to update
  1758.     the offset in the past history to reflect the changes in the various offset.
  1759. */
  1760.  
  1761. void cmClosePastDataGap(ContainerPtr container, CM_ULONG offset, CM_ULONG size)
  1762. {
  1763.     PastUpdate                         *pastUpdate = (PastUpdate *) cmGetListHead(&container->tmpList);
  1764.     CM_CHAR                                ctlByte;
  1765.     OffsetSizePair                *offsetSizePair;
  1766.     CM_ULONG                            n;
  1767.  
  1768.     while (pastUpdate) {
  1769.         ctlByte = pastUpdate->ctlByte;
  1770.  
  1771.         switch (ctlByte) {
  1772.             case DeleteData: 
  1773.                 if (((PastDeleteData *)pastUpdate)->offset > offset)
  1774.                     ((PastDeleteData *)pastUpdate)->offset -= size;
  1775.                 break;
  1776.             case InsertData1:
  1777.                 if (((PastInsertData *)pastUpdate)->offset > offset)
  1778.                     ((PastInsertData *)pastUpdate)->offset -= size;
  1779.                 break;
  1780.             case InsertDataN: 
  1781.                 n = ((PastInsertData *)pastUpdate)->insertCount;
  1782.                 offsetSizePair = (OffsetSizePair *)((char *)pastUpdate + sizeof(PastUpdate)+sizeof(CM_ULONG)+sizeof(CM_ULONG));;
  1783.                 while (n--) {
  1784.                     if (offsetSizePair->offset > offset)
  1785.                         offsetSizePair->offset -= size;
  1786.                     offsetSizePair++;
  1787.                 }
  1788.                 break;
  1789.             } /* switch */
  1790.         pastUpdate = (PastUpdate *) cmGetNextListCell(pastUpdate);
  1791.     }
  1792. }
  1793.  
  1794. /*-----------------------------------------------------------------*
  1795.  | generatePastValueUpdates - generate the value update once again |
  1796.  *-----------------------------------------------------------------*
  1797.  
  1798.     When we are merging updating containers, we may need to generate update we had
  1799.     previously. We have various cases we have to deal with.
  1800.     
  1801.     If the update occurs on a value in the previous container but is not touched in the
  1802.     latest round of change, we can just generate them again.
  1803.     
  1804.     If the value no longer exists, then we can ignore the previous update, unless the
  1805.     previous update is a DeleteValue, then we have to generate the DeleteValue again.
  1806.     We also have to watch out where a create occurs after the delete.
  1807.     
  1808.     SetInfoType has some unique problem, since the type has been changed we need to
  1809.     search it differently to make sure it is still here.
  1810.     
  1811.     Certain new update will make old update superfluous, e.g. if the value is now an
  1812.     immediate value, we change ingore any past data edits.
  1813. */
  1814.  
  1815. static void CM_NEAR generatePastValueUpdates(TOCObjectPtr theObject, ContainerPtr container, ContainerPtr targetContainer, void *ioBuffer)
  1816. {
  1817.     PastUpdate                         *pastUpdate = (PastUpdate *) cmGetListHead(&targetContainer->tmpList);
  1818.     PastUpdate                        *next;
  1819.     CMObjectID                        currPropertyID=0;
  1820.     CM_ULONG                            n;
  1821.     CM_CHAR                                ctlByte;
  1822.     TOCPropertyPtr                 theProperty;
  1823.     TOCValueHdrPtr                 theValueHdr;
  1824.     OffsetSizePair                *offsetSizePair;
  1825.     CM_ULONG                            *baseTypePtr;
  1826.     CMBoolean                            becomeImmediate;
  1827.     
  1828.     while ((pastUpdate) && (theObject->objectID != pastUpdate->objectID))
  1829.         pastUpdate = (PastUpdate *) cmGetNextListCell(pastUpdate);
  1830.  
  1831.     while ((pastUpdate) && (theObject->objectID == pastUpdate->objectID)) {
  1832.         next = (PastUpdate *) cmGetNextListCell(pastUpdate);
  1833.         ctlByte = pastUpdate->ctlByte;
  1834.         if (ctlByte >= RemovedValue) {
  1835.             theProperty = cmGetObjectProperty(theObject, pastUpdate->propertyID);
  1836.             if (theProperty == NULL)
  1837.                 goto generateDone;                    /* property is gone, no need for old update                 */
  1838.  
  1839.             /* If we are doing a delete value, we don't expect to see the valueHdr. We may         */
  1840.             /* still see the value header in delete value because the types has been added        */
  1841.             /* back. We really don't care either way and we can generate the delete value            */
  1842.             /* update. For the other cases, if the value header is gone, this mean that the        */
  1843.             /* type has been deleted and we can ignore the old updates since if the value    is    */
  1844.             /* deleted eventually, who cares about the old changes. SetInfoType is a special    */
  1845.             /* case since we need to check the new type rather than the old because it is         */
  1846.             /* already converted over to the new type. However in this version we do not             */
  1847.             /* support merge of type changes, so we can postpone that to the future version.    */
  1848.             if (ctlByte != DeleteValue) {
  1849.                 theValueHdr = (TOCValueHdrPtr)cmGetPropertyType(theProperty, pastUpdate->typeID);
  1850.                 if (theValueHdr == NULL)
  1851.                     goto generateDone;                                    /* value is gone, no need for old update     */
  1852.                 if (theValueHdr->touch) {
  1853.                     /* If the value now becomes an immediate value, then we can ignore all                 */
  1854.                     /* previous write and delete, so we want to check if it just become an                */
  1855.                     /* immediate value                                                                                                                        */
  1856.                     becomeImmediate = (theValueHdr->touch->touchFlags & TouchedImmediate) &&
  1857.                                                         (theValueHdr->size <= 4) &&
  1858.                                                         (((TOCValuePtr) cmGetListHead(&theValueHdr->valueList))->flags & kCMImmediate);
  1859.                     switch (ctlByte) {
  1860.                         case ReplaceImmediate:
  1861.                             if (becomeImmediate)
  1862.                                 goto generateDone;                        /* will have new value, discard the old value */
  1863.                             break;
  1864.                         case DeleteData:
  1865.                         case InsertData1:
  1866.                         case InsertDataN:
  1867.                             if (becomeImmediate)
  1868.                                 goto generateDone;                            /* then no need to do it now                      */
  1869.                             else if (theValueHdr->touch->touchFlags & TouchedEdited)  {
  1870.                                 next = putBackOldOffset(targetContainer, theObject, (PastDeleteData *)pastUpdate);
  1871.                                 goto generateDone;                            /* then no need to do it now                      */
  1872.                             } 
  1873.                             break;
  1874.                         case SetInfoedValue:
  1875.                             /* If we go from previously from a to b, now we go from b to c                        */
  1876.                             /* the whole thing should be reorded as a to c, we change the preious         */
  1877.                             /* setinfo and discard the latest setinfo                                                                    */
  1878.                             /* However, since we are not supporting this in this new version, we don't*/
  1879.                             /* need to worry about this for now and we can just use the new update and*/
  1880.                             /* discard the old update                                                                                                    */
  1881.                             if (theValueHdr->touch->touchFlags & TouchedSetinfoed) {
  1882.                                 goto generateDone;                            /* then no need to do it now                      */
  1883.                             }
  1884.                             break;
  1885.                         case ReplaceBaseType:
  1886.                             if (theValueHdr->touch->touchFlags & TouchedBaseType)
  1887.                                 goto generateDone;                    /* will have new base, discard the old base    */
  1888.                     } /* switch */
  1889.                 }
  1890.             }
  1891.             if (currPropertyID != pastUpdate->propertyID) {        /* new property...                            */
  1892.                 currPropertyID = pastUpdate->propertyID;
  1893.                 PUT1(NewProperty     , ioBuffer);                                    /* <new prop>                                        */
  1894.                 PUT4(currPropertyID, ioBuffer);                                    /*            P                                    */
  1895.                 PUT4(pastUpdate->typeID , ioBuffer);                        /*              T                                */
  1896.             } else {
  1897.                 PUT1(NewType             , ioBuffer);                                    /* <new type>                                        */
  1898.                 PUT4(pastUpdate->typeID , ioBuffer);                        /*            T                                    */
  1899.             }
  1900.             /* We need to regenerate the update instruction again in the new merged container */
  1901.             PUT1(ctlByte, ioBuffer);                                                                         /* <OpCode>                        */
  1902.             switch (ctlByte) {
  1903.                 case SetInfoedValue:
  1904.                     PUT4(((PastSetInfoedValue *)pastUpdate)->newType, ioBuffer);/*        T                        */
  1905.                     PUT4(((PastSetInfoedValue *)pastUpdate)->generation, ioBuffer);/*        g                    */
  1906.                     break;
  1907.                 case DeleteData: 
  1908.                     PUT4(((PastDeleteData *)pastUpdate)->offset, ioBuffer);                /*      offset            */
  1909.                     PUT4(((PastDeleteData *)pastUpdate)->size, ioBuffer);                    /*   amount            */
  1910.                     break;
  1911.                 case InsertData1:
  1912.                     PUT4(((PastInsertData *)pastUpdate)->insertPoint, ioBuffer);    /*     insertPoint    */
  1913.                     PUT4(((PastInsertData *)pastUpdate)->offset, ioBuffer);                /*    offset            */
  1914.                     PUT4(((PastInsertData *)pastUpdate)->size, ioBuffer);                    /*    amount            */
  1915.                     break;
  1916.                 case InsertDataN: 
  1917.                     PUT4(((PastInsertData *)pastUpdate)->insertPoint, ioBuffer);    /*     insertPoint    */
  1918.                     n = ((PastInsertData *)pastUpdate)->insertCount;
  1919.                     PUT4(n, ioBuffer);                                                                                        /*         n                */
  1920.                     offsetSizePair = (OffsetSizePair *)((char *)pastUpdate + sizeof(PastUpdate)+sizeof(CM_ULONG)+sizeof(CM_ULONG));;
  1921.                     while (n--) {
  1922.                         PUT4(offsetSizePair->offset, ioBuffer);
  1923.                         PUT4(offsetSizePair->size, ioBuffer);
  1924.                         offsetSizePair++;
  1925.                     }
  1926.                     break;
  1927.                 case ReplaceImmediate:
  1928.                     PUT1(((PastReplaceImmediate *)pastUpdate)->size, ioBuffer);        /*         length         */
  1929.                     PUT4Direct(((PastReplaceImmediate *)pastUpdate)->newImmediate, ioBuffer);    /* value    */
  1930.                     break;
  1931.                 case ReplaceBaseType: 
  1932.                     n = ((PastReplaceBaseType *)pastUpdate)->nBases;
  1933.                     PUT2(n, ioBuffer);                                                                                        /*         n                */
  1934.                     baseTypePtr = (CM_ULONG *)((char *)pastUpdate + sizeof(PastReplaceBaseType));;
  1935.                     while (n--) {
  1936.                         PUT4(*baseTypePtr, ioBuffer);                                                                /* base type ID    */
  1937.                         baseTypePtr++;
  1938.                     }
  1939.                     break;
  1940.                 case DeleteValue:
  1941.                     /* Need to do nothing beyond the OpCode                                                                                */
  1942.                     break;
  1943.                 } /* switch */
  1944.             generateDone: CMfree(cmDeleteListCell(&targetContainer->tmpList, pastUpdate));
  1945.         }
  1946.         else {    /* this is delete update, so we are done with generating past edit updates    */
  1947.             break;
  1948.         }
  1949.         pastUpdate = next;
  1950.     }
  1951.     
  1952.     theObject->objectFlags &= ~EditedRecently;
  1953. }
  1954.  
  1955. /*-------------------------------------------------------------------*
  1956.  | generatePastDeleteUpdates - generate the delete update once again |
  1957.  *-------------------------------------------------------------------*
  1958.  
  1959.     When we are merging updating containers, we may need to generate update we had
  1960.     previously.
  1961.     
  1962.     In general we just generate the same instruction again. The only things to watch out
  1963.     for is that delete property is not necessary if the object is gone.
  1964.     
  1965. */
  1966.  
  1967. static void CM_NEAR generatePastDeleteUpdates(TOCObjectPtr theObject, ContainerPtr container, ContainerPtr targetContainer, void *ioBuffer)
  1968. {
  1969.     PastUpdate                     *pastUpdate = (PastUpdate *) cmGetListHead(&targetContainer->tmpList);
  1970.     PastUpdate                    *next;
  1971.     CM_ULONG                        curObjectID = 0;
  1972.     TouchedListEntryPtr touch;
  1973.     
  1974.     while ((pastUpdate) && (theObject->objectID != pastUpdate->objectID))
  1975.         pastUpdate = (PastUpdate *) cmGetNextListCell(pastUpdate);
  1976.  
  1977.     while ((pastUpdate) && (theObject->objectID == pastUpdate->objectID)) {
  1978.         next = (PastUpdate *) cmGetNextListCell(pastUpdate);
  1979.         if ((pastUpdate->ctlByte == DeleteProperty1) &&
  1980.                 ((theObject->objectFlags & DeletedObject) == 0)) {                /* not yet deleted        */
  1981.  
  1982.             /* check to see if the property is deleted, if it is then no need to do it here        */
  1983.             touch = (TouchedListEntryPtr)cmGetListHead(&theObject->touchedList);
  1984.             while (touch) {                                                                        
  1985.                 if ((touch->propertyID == pastUpdate->propertyID) 
  1986.                             && (touch->touchFlags & TouchedDeletedProperty))
  1987.                     break;
  1988.                 touch = (TouchedListEntryPtr)cmGetNextListCell(touch);
  1989.             } /* while */
  1990.             
  1991.             
  1992.             if (!touch)    {            /* we don't have a touch that delete this property                             */                                                                                        
  1993.                 if (curObjectID != theObject->objectID) {
  1994.                     PUT1(DeleteProperty1, ioBuffer);                                                 /* <OpCode>                        */
  1995.                     PUT4(theObject->objectID, ioBuffer);                                        /*                 objectID        */
  1996.                 }
  1997.                 else
  1998.                     PUT1(DeleteProperty2, ioBuffer);                                                 /* <OpCode>                        */
  1999.                 PUT4(pastUpdate->propertyID,  ioBuffer);                                    /*                 propertyID    */
  2000.             }
  2001.         }
  2002.         CMfree(cmDeleteListCell(&targetContainer->tmpList, pastUpdate));
  2003.         pastUpdate = next;
  2004.     }    
  2005. }
  2006.  
  2007. /*--------------------------------------------------------------------------*
  2008.  | generateValueUpdates - main control generate value updating instructions |
  2009.  *--------------------------------------------------------------------------*
  2010.  
  2011.  This routine is called only by cmGenerateUpdates() to control the generation of value
  2012.  updating instructions.  The entire touched list chain is walked and all non-deleted
  2013.  objects on the chain have their respective touched lists processed.  Property deletes are
  2014.  skipped on the touched lists.  Thus all that are processed here are touched values and
  2015.  their update instructions generated.
  2016.  
  2017.  The update instructions for all the touched values of any one object are generated as data
  2018.  for a special "updating" property of that object.  That property and value are defined
  2019.  here.  The TOC has not yet been written, so this is permissible.  It thus goes into the
  2020.  TOC like any other value and will be written to the container when the time comes.  At
  2021.  open time, the TOC reader can look for this special property to get at the object's
  2022.  update list.
  2023.  
  2024.  All touched value list entries are freed after processing.  Thus all that will be left
  2025.  on the touched chain are touched deleted objects and non-deleted objects with touched
  2026.  chains consisting solely of "deleted property" entries.
  2027.  
  2028.  False is returned for failure, and true for success.
  2029.  
  2030.  The instructions are generated in the following general sequences:
  2031.  
  2032.                                        --                                    --
  2033.                                                                        |  ( <new prop> P T <v> [params...] )  |
  2034.         <new prop> P T <v> [params...]  |  <                                >  | ... <end>
  2035.                                           |  ( <new type> T <v> [params...]   )  |
  2036.                                       --                                    --
  2037.  
  2038.  where <new prop> = NewProperty control byte
  2039.               P, T                = property and type IDs (4 bytes)
  2040.              <v>        = value update control byte (see genValueUpdateInstructions())
  2041.              params...     = parameters as a function of <v> (see genValueUpdateInstructions())
  2042.              <new type> = NewType control byte
  2043.              <end>            = EndUpdates control byte to mark the end of the updating sequence
  2044.              
  2045.  Brackets mean optional, "..." means repeating sequences of the preceding, and braces
  2046.  mean alternatives (sorry, it's the best I can do with this font).
  2047.  
  2048.  What the sequence does is generate "<v> [params...]" for every value update.  However,
  2049.  only the P is generated as new properties are encountered for the current object.  The
  2050.  object isn't needed since this set of instructions is just the instructions dealing with
  2051.  one object.  Remember, the instructions are value data for a value belonging to the
  2052.  special "updating" property of the object.  
  2053.  
  2054.  Note, the "<v> [params...]" is a recursive definition, in that the "params..." itself may
  2055.  be a "<v> [params...]" sequence.
  2056.  
  2057.  All this encoding is done to minimize generated data space for this stuff but still make
  2058.  it easy to interpret at open time.  Thus NewProperty implies a PT immediately follow.
  2059.  NewType implies only a T follows.  This can happen when sequences of touches for an
  2060.  object all refer to the same property.  This is not the absolute minimum generation,
  2061.  however, since touch list entries are NOT sorted by property.
  2062. */
  2063.  
  2064. static CMBoolean CM_NEAR generateValueUpdates(ContainerPtr container, ContainerPtr targetContainer, void *ioBuffer)
  2065. {
  2066.     TOCObjectPtr                 thePrevObject, theObject, nextTouchedObject;
  2067.     TouchedListEntryPtr nextTouch, touch;
  2068.     TOCValueHdrPtr            updatesValueHdr;
  2069.     TOCPropertyPtr            nextProperty;
  2070.     CMObjectID                    currPropertyID, nextPropertyID;
  2071.     CMBoolean                        noTouches;
  2072.     ContainerPtr                updatingContainer = container->updatingContainer;
  2073.     TOCValueHdrPtr            theValueHdr;
  2074.     TOCValuePtr                    theValue;
  2075.     CMBoolean                     editedRecently;
  2076.     
  2077.     /* Walk the entire touched chain looking for non-deleted touched objects...                        */
  2078.     
  2079.     thePrevObject = NULL;                                                                    /* initially no previous object    */
  2080.     theObject = updatingContainer->touchedChain;                    /* point to 1st touched object    */
  2081.     
  2082.     while (theObject != NULL) {                                                         /* walk touched chain...                */
  2083.         nextTouchedObject = theObject->nextTouchedObject;        /* get ptr to next object early    */
  2084.         
  2085.         if ((theObject->objectFlags & DeletedObject) == 0) {/* non-deleted objects only...    */
  2086.             
  2087.             /* find out if we touch it in the most recent container                                                        */
  2088.             editedRecently = (theObject->objectFlags & EditedRecently) ? true : false;
  2089.             /* Here we have a non-deleted object.  Generally, being that it's on the touched    */
  2090.             /* chain, it should have a touched list.  But being paranoid, we never assume         */
  2091.             /* that.  We do have to make sure that the touched list does not consist solely     */
  2092.             /* of "deleted property" entries.  At the very least we have to skip to the first    */
  2093.             /* value touch on the list.  Further, moves out of an object could leave that         */
  2094.             /* object on the touched chain, but with no touched list entries if that was the    */
  2095.             /* only thing done to the object.  Those kind of objects we just want to delete.    */
  2096.             
  2097.             touch = (TouchedListEntryPtr)cmGetListHead(&theObject->touchedList);
  2098.             noTouches = (CMBoolean)(touch == NULL);                            /* remember that list is empty    */ 
  2099.             
  2100.             while (touch != NULL) {                                                        /* look for first value touch...*/
  2101.                 nextTouch = (TouchedListEntryPtr)cmGetNextListCell(touch);
  2102.                 if ((touch->touchFlags & TouchedDeletedProperty) == 0) { /* not delete property    */
  2103.                     if (touch->theValueHdr->theProperty)                    /* and we staill have property     */
  2104.                         if (touch->theValueHdr->container->depth == 0) {
  2105.                             /* After merging, targetContainer is part of the updating container, so any        */
  2106.                             /* value in it does not need to generate any update instruction.                            */
  2107.                             touch->theValueHdr->touch = NULL;
  2108.                             CMfree(cmDeleteListCell(&theObject->touchedList, touch));
  2109.                         }
  2110.                         else
  2111.                             break;                                                                            /* ...got 'em!                                    */
  2112.                     else    {                                            /* property is gone, so we can ignore this change    */
  2113.                         touch->theValueHdr->touch = NULL;
  2114.                         CMfree(cmDeleteListCell(&theObject->touchedList, touch));
  2115.                     }
  2116.                 }
  2117.                 touch = nextTouch;
  2118.             }
  2119.             
  2120.             /* If we are "looking" at the first value touch on its touched list, we know we        */
  2121.             /* have some updating instructions to generate for its object.  Thus we must             */
  2122.             /* create the special "updating" property for this object (basically done as a        */
  2123.             /* CMNewValue()).  Then we can start the  generation sequence.                                        */
  2124.                 
  2125.             /* For merge we need to handle it if update is in the new container or in the            */
  2126.             /* most recent container.                                                                                                                    */
  2127.             if ((touch != NULL) || editedRecently) {                    /* if at least 1 value touched    */
  2128.                 theObject = cmDefineObject(container,                        /* ...create "updates" property    */
  2129.                                                                      theObject->objectID,
  2130.                                                                      CM_StdObjID_ValueUpdates,
  2131.                                                                      CM_StdObjID_UpdatesData,
  2132.                                                                      NULL, container->generation, 0, 
  2133.                                                                      ObjectObject, &updatesValueHdr);
  2134.                 if (theObject == NULL) return (false);                    /* failure (shit!)                            */
  2135.                 
  2136.                 cmNewBufferedOutputData(ioBuffer, updatesValueHdr); /* init for buffered output    */
  2137.  
  2138.                 /* We try to put out update from the past first.                                                                */
  2139.                 if ((targetContainer) && (editedRecently))
  2140.                     generatePastValueUpdates(theObject, container, targetContainer, ioBuffer);
  2141.                 /* The property's value header is used as the "refNum" for all output operations*/
  2142.                 /* done by the output routine (writeUpdate()).  The updating is buffered and         */
  2143.                 /* written as value data for the new value.                                                                            */
  2144.                 
  2145.                 /* Generate the appropriate value update sequences for each touched value,             */
  2146.                 /* skipping any further touched deleted properties we find on the list. We only    */
  2147.                 /* skipped initial ones above. That does not mean there aren't any further ones    */
  2148.                 /* on this list beyond the first touched value!                                                                  */
  2149.                 
  2150.                 /* For entries we do process, a check must be made each time to see if the             */
  2151.                 /* property has changed. If it has (and the first time is always "change") then    */
  2152.                 /* a "<new prop> PT" sequence is generated. Otherwise a "<new type> T" sequence */
  2153.                 /* is generated, since we always have a new type for each separate touched list */
  2154.                 /* entry.                                                                                                                                                */
  2155.                 
  2156.                 /* Note, there is a non-obvious subtility in the way the "P" is chosen in the     */
  2157.                 /* "<new prop> PT" sequence.  This sequence, incombination with the object they    */
  2158.                 /* are data for (the "update" property value data for the "owning" object), are    */
  2159.                 /* used to address the receiver of the actions by the updating instructions.        */
  2160.                 /* That MUST address where the value is NOW.  If a value wasn't moved, the             */
  2161.                 /* property in the touch list entry agree with where the value is now.  But if     */
  2162.                 /* it IS moved, there is no guarantee it is (and usually won't be) in the same     */
  2163.                 /* property!  So the property ID in the touched list entry is NOT used here to     */
  2164.                 /* address the value.  Rather, the value header itself (it "knows" where it is)    */
  2165.                 /* is used to get the property ID of the property is is presently on.                        */
  2166.                 
  2167.                 /* The SPECIFIC value instruction sequences are generated separately by                    */
  2168.                 /* genValueUpdateInstructions().  That routine assumes the "<new prop> PT" or     */
  2169.                 /* "<new type> T" prefix is generated here.  But, as a safety check we do NOT        */
  2170.                 /* want to generate the prefix if genValueUpdateInstructions() CAN'T generate a    */
  2171.                 /* "<v> [params...]" sequence.  It can't, if for some reason, the touched value    */
  2172.                 /* header has no values!  For "removed" and "deleted value", it doesn't matter.    */
  2173.                 /* But for every other kind of touch it does.  So, just to be     safe, we do this */
  2174.                 /* check here and not generate the prefix or call genValueUpdateInstructions()     */
  2175.                 /* if the test fails.                                                                                                                        */
  2176.                 
  2177.                 currPropertyID = 0;                                                            /* force first property change    */
  2178.                 
  2179.                 while (touch != NULL) {                                                    /* look at all of 'em                        */
  2180.                     nextTouch = (TouchedListEntryPtr)cmGetNextListCell(touch); /* need next now        */
  2181.                     
  2182.                     if ((touch->touchFlags & TouchedDeletedProperty) == 0)  /* not "deleted prop"    */
  2183.                         if ((touch->touchFlags & (TouchedRemoved | DeleteValue)) != 0 ||
  2184.                                 (touch->theValueHdr != NULL && cmCountListCells(&touch->theValueHdr->valueList) > 0)) {
  2185.                             nextProperty = touch->theValueHdr->theProperty;
  2186.                             if (nextProperty)    {                                                /* we still have this property     */
  2187.                                 /* If we are merging container, values will be moved into    the updating    */
  2188.                                 /* container. Since modification to the values in the updating                     */
  2189.                                 /* is not recorded as update, then under these circumstances we do not    */
  2190.                                 /* generate updates when there is merging. So we first check to make        */
  2191.                                 /* sure we are not merging and the value header is not in the immediate    */
  2192.                                 /* preceding container.                                                                                                    */
  2193.                                 theValueHdr = touch->theValueHdr;
  2194.                                 if ((theValueHdr == NULL) ||
  2195.                                         ((theValueHdr->container->updatingContainer->useFlags & kCMMerging) == 0) ||
  2196.                                         (theValueHdr->container->depth != 1)) {
  2197.                                 nextPropertyID = nextProperty->propertyID;
  2198.                                 if (nextPropertyID != currPropertyID) {        /* new property...                            */
  2199.                                     currPropertyID = nextPropertyID;
  2200.                                     PUT1(NewProperty     , ioBuffer);                    /* <new prop>                                        */
  2201.                                     PUT4(currPropertyID, ioBuffer);                    /*            P                                    */
  2202.                                     PUT4(touch->typeID , ioBuffer);                    /*              T                                */
  2203.                                 } else {
  2204.                                     PUT1(NewType             , ioBuffer);                    /* <new type>                                        */
  2205.                                     PUT4(touch->typeID , ioBuffer);                    /*            T                                    */
  2206.                                 }
  2207.                             
  2208.                                 /* The specific value updating sequence, "<v> [params...]", is generated*/
  2209.                                 /* separately.  We got enough on our hands here as it is!  Remember, the*/
  2210.                                 /* "<v> [params...]" sequence is recursive so that the "params..." may         */
  2211.                                 /* itself container "<v> [params...]" sequences for additional updates         */
  2212.                                 /* for the same value.                                                                                                        */
  2213.                                 
  2214.                                 genValueUpdateInstructions(touch, ioBuffer); 
  2215.                                 }
  2216.                             }
  2217.                             
  2218.                             /* Now that the touched value is fully processed, we can delete its             */
  2219.                             /* touched entry from the touched chain.                                                                  */
  2220.  
  2221.                             /* we also delete it if property is gone since the update is not needed        */
  2222.                             
  2223.                             if (touch->theValueHdr)
  2224.                                 touch->theValueHdr->touch = NULL;
  2225.                             CMfree(cmDeleteListCell(&theObject->touchedList, touch));  /* poof!                */
  2226.                         } /* value update */
  2227.  
  2228.                     touch = nextTouch;                                                        /* next touched chain entry            */
  2229.                 };                                                                                            /* look at all of 'em                        */
  2230.                 
  2231.                 /* At this point all the updating instructions for all touched values for the        */
  2232.                 /* current object have been generated and written as value data for the special    */
  2233.                 /* updates" property of that object.  All that remains to do is write an "end-    */
  2234.                 /* of-sequence" marker byte and to flush the buffer.                                                        */
  2235.                 
  2236.                 PUT1(EndUpdates, ioBuffer);                                            /* add "end-of-update-sequence" */
  2237.                 cmFlushOutputBuffer(ioBuffer);                                    /* end-of-updates                                */
  2238.                 
  2239.                 /* If combine two embedded containers into one, we need to offset the values        */
  2240.                 if (container->mergeInSize) {
  2241.                     theValue = (TOCValuePtr)cmGetListHead(&updatesValueHdr->valueList);
  2242.                     while (theValue) {
  2243.                         if ((theValue->flags & kCMImmediate) == 0)
  2244.                             theValue->value.notImm.value += container->mergeInSize;
  2245.                         theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  2246.                     }
  2247.                 }
  2248.                 
  2249.                 /* The object may be removed from the touched chain if its touched chain is         */
  2250.                 /* empty.  It won't be empty if there are any "deleted property" entries which    */
  2251.                 /* we skipped.                                                                                                                                    */
  2252.                 
  2253.                 /* remove if no touched list and not touched (deleted) recently                                    */
  2254.                 if ((cmIsEmptyList(&theObject->touchedList)) && (theObject->objectFlags & DeletedRecently == 0))
  2255.                     cmDeleteObjFromTouchedChain(updatingContainer, theObject, thePrevObject);
  2256.                 else                                                                                        /* if "deleted property" entries*/
  2257.                     thePrevObject = theObject;                                        /* ...leave it on touched list    */
  2258.             } else if (noTouches && ((theObject->objectFlags & DeletedRecently) == 0)) 
  2259.                 /* if empty touch list we can remove it from the touch chain                                        */
  2260.                 cmDeleteObjFromTouchedChain(updatingContainer, theObject,thePrevObject);
  2261.             else                                                                                             /* if all property deletes...        */
  2262.                 thePrevObject = theObject;                                            /* ...leave obj on touched chain*/
  2263.         } else                                                                                            /* if deleted object...                    */
  2264.             thePrevObject = theObject;                                                /* ...leave it on touched chain */
  2265.         
  2266.         theObject = nextTouchedObject;                                            /* process next touched object    */
  2267.     } /* while (theObject)... */
  2268.     
  2269.     return (true);                                                                                /* success                                            */
  2270. }
  2271.  
  2272. /*-----------------------------------------------------------------------------*
  2273.  | makeDeleteValue - make the value that is the list of all the delete updates |
  2274.  *-----------------------------------------------------------------------------*
  2275.   
  2276.  This routine is called only by makeDeleteValue to generate the value that is
  2277.  going to hold all the instructions for the delete updates. 
  2278. */
  2279.  
  2280. static TOCValueHdrPtr CM_NEAR makeDeleteValue(ContainerPtr container, void *ioBuffer)
  2281. {
  2282.     TOCValueHdrPtr            deletesValueHdr = NULL;
  2283.     TOCObjectPtr                 theTOCobject;
  2284.     void                                 *toc;
  2285.  
  2286.     toc = container->toc;                                                        /* create TOC #1 property/value    */
  2287.     container->toc = container->privateTOC;                    /* IN THE PRIVATE CONTAINER TOC    */
  2288.     
  2289.     theTOCobject = cmDefineObject(container,                /* ...create "updates" property    */
  2290.                                                                 CM_StdObjID_TOC, CM_StdObjID_TOC_DeleteList,
  2291.                                                                 CM_StdObjID_UpdatesData, NULL,
  2292.                                                                 container->generation, 0,
  2293.                                                                 (ObjectObject | ProtectedObject),
  2294.                                                                 &deletesValueHdr);
  2295.     
  2296.     container->toc = toc;                                                        /* restore current TOC                    */
  2297.     if (theTOCobject) {                                                            /* if not failure, continue            */
  2298.         container->deletesValueHdr = deletesValueHdr;    /* save "refNum" for the world    */
  2299.         
  2300.         cmNewBufferedOutputData(ioBuffer, deletesValueHdr);/* init for buffered output*/
  2301.     }
  2302.     return deletesValueHdr;
  2303. }
  2304.  
  2305. /*-----------------------------------------------------------------------------------*
  2306.  | generateDeleteUpdates - generate object and property delete updating instructions |
  2307.  *-----------------------------------------------------------------------------------*
  2308.   
  2309.  This routine is called only by cmGenerateUpdates() to control the generation of object
  2310.  and property deletion updating instructions.  The entire touched list chain is walked and
  2311.  all deleted objects on the chain or non-deleted objects with a touched list are processed.
  2312.  This is the second walk of the touched chain.  The first walk was done with
  2313.  generateValueUpdates() above.  All that should remain on the touched chain are deleted
  2314.  objects (with possibly a touched chains of "removed" and "deleted value" entries) and 
  2315.  non-deleted objects with "deleted property" entries.
  2316.   
  2317.  False is returned for failure, and true for success.
  2318.  
  2319.  The reason a deleted object can still have a touched chain of its own is that when an
  2320.  object is deleted, we explicitly leave "removed" entries on the chain but delete every-
  2321.  thing else.  By doing this, values moved out of the object prior to its deletion, have
  2322.  "inserted" entries with back pointers pointing to their corresponding "removed" entry.
  2323.  If these values are then deleted, they better have a valid "removed" entry to point to
  2324.  so that it can be changed to a "deleted value" entry.  That's how "deleted value" entries
  2325.  can also be on the chain.  None of this causes problems, because if we see a deleted 
  2326.  object on the touched chain, we generate the "delete object" updating instruction and
  2327.  ignore the touched list except to free up its memory.
  2328.  
  2329.  Note, we do not remove the remaining touched objects from the touched chain.  It's 
  2330.  unnecessary since we will never look at the touched chain again after this. The container
  2331.  is about to be closed.
  2332.  
  2333.  The deletion update instructions are generated as a single sequence for a value defined 
  2334.  as a special "delete list" property of TOC #1 defined here. This is different from value
  2335.  updates where the update instructions for a particular object are "attached" to that
  2336.  object with a special property for it.  The TOC has not yet been written, so this it is
  2337.  permissible to still create TOC objects.  It thus goes into the TOC like any other value
  2338.  and will be written to the container when the time comes.  At open time, the TOC reader
  2339.  can look for this special property to get at the object's update list.
  2340.  
  2341.  The deletions are processed and kept separate because they must be used at open time AFTER
  2342.  all the value updates are applied.  The reason for this is mainly because of "moves".  A
  2343.  value could be moved from its original position and then its original object deleted.  If
  2344.  we didn't delay the deletes, there potentially may not be any source to move the value
  2345.  from!
  2346.  
  2347.  The deletion instructions are generated in the following general sequence:
  2348.  
  2349.         ( <del obj> O                         )
  2350.         <                                     > ... <end>
  2351.         ( <del prop1> O P [<del prop2> P ...] )
  2352.  
  2353.  where <del obj>   = DeleteObject control byte
  2354.              O, P              = object and property IDs (4 bytes)
  2355.              <del prop1> = DeleteProperty1 control byte (property for another object)
  2356.              <del prop2> = DeleteProperty2 control byte (property for same object)
  2357.              <end>            = EndUpdates control byte to mark the end of the updating sequence
  2358.  
  2359.  Brackets mean optional, "..." means repeating sequences of the preceding, and braces
  2360.  mean alternatives (sorry, it's the best I can do with this font).
  2361.  
  2362.  What this sequence does is generate "delete object" or "delete property" for a new or
  2363.  the same object.
  2364. */
  2365.  
  2366. static CMBoolean CM_NEAR generateDeleteUpdates(ContainerPtr container, ContainerPtr targetContainer, void *ioBuffer)
  2367. {
  2368.     TOCObjectPtr                 theObject;
  2369.     TouchedListEntryPtr nextTouch, touch;
  2370.     TOCValueHdrPtr            deletesValueHdr;
  2371.     CMBoolean                        deletedObject, first;
  2372.     TOCValuePtr                    theValue;
  2373.     CMBoolean                     deletedRecently;
  2374.     PastUpdate                     *pastUpdate;
  2375.     PastUpdate                    *next;
  2376.     
  2377.  
  2378.     /* Walk the entire touched chain looking for everthing remaining there.  The "refNum"    */
  2379.     /* for the special TOC #1 property is kept in deletesValueHdr and it is NULL until we    */
  2380.     /* actually find something to generate.                                                                                                */
  2381.     
  2382.     deletesValueHdr = NULL;                                                                /* NULL ==> no TOC #1 value yet    */
  2383.     theObject = container->updatingContainer->touchedChain;/* point to 1st touched object    */
  2384.         
  2385.     while (theObject != NULL) {                                                        /* walk touched chain...                */
  2386.         
  2387.         /* Find out if we did a delete update in the most recent old container,                            */
  2388.         deletedRecently = (theObject->objectFlags & DeletedRecently) ? true : false;
  2389.  
  2390.         /* We need to know if we're going to generate any deletion instructions for the         */
  2391.         /* current object. If we have a deleted object, it's on the touched chain, so that's*/
  2392.         /* sufficient to know we need to generate a delete object instruction. On the other    */
  2393.         /* hand, if we have a non-deleted object, just to be safe, we scan its touched list    */
  2394.         /* to see if there are any "deleted property" entries.  If there are, then we know    */
  2395.         /* for that case we got something to generate.                                                                          */
  2396.         
  2397.         /* Note, for deleted objects, we always make sure all the remaining touched list         */
  2398.         /* entries are freed.  We no longer need them.                                                                            */
  2399.         
  2400.         deletedObject = (CMBoolean)((theObject->objectFlags & DeletedObject) != 0);
  2401.         touch = (TouchedListEntryPtr)cmGetListHead(&theObject->touchedList);
  2402.         
  2403.         if (deletedObject)                                                                    /* if deleted object...                    */
  2404.             while (touch) {                                                                        /* ...free the touched list            */
  2405.                 nextTouch = (TouchedListEntryPtr)cmGetNextListCell(touch);
  2406.                 if (touch->theValueHdr)
  2407.                     touch->theValueHdr->touch = NULL;
  2408.                 CMfree(cmDeleteListCell(&theObject->touchedList, touch));
  2409.                 touch = nextTouch;
  2410.             } /* while */
  2411.         else                                                                                                /* if not deleted object...            */
  2412.             while (touch != NULL) {                                                        /* look for 1st deleted property*/
  2413.                 if ((touch->touchFlags & TouchedDeletedProperty) != 0) 
  2414.                 nextTouch = (TouchedListEntryPtr)cmGetNextListCell(touch);
  2415.                 /* If the property is created in the container to be merged, then after                    */
  2416.                 /* merging it is in the update container and  we don't need to generate a             */
  2417.                 /* delete property instruction for deleting property in the update container.        */
  2418.                 if ((theObject->container->updatingContainer->useFlags & kCMMerging) && (touch->typeID == 0)) {
  2419.                     if (touch->theValueHdr)
  2420.                         touch->theValueHdr->touch = NULL;
  2421.                     CMfree(cmDeleteListCell(&theObject->touchedList, touch));
  2422.                 }
  2423.                 else
  2424.                     break;                                                                                /* ...got 'em!                                    */
  2425.                 touch = nextTouch;
  2426.             }
  2427.         
  2428.         /* If the object is in target container, then with merging the object is considered.*/
  2429.         /* to be a new object, so we don't need to generate updates.                                                */
  2430.         if (theObject->container == targetContainer)
  2431.             deletedObject = false;
  2432.         
  2433.         /* At this point we know whether we got somthing to generate.  It's either for a         */
  2434.         /* deleted object, or a deleted property.  If there's nothing to generate, just go    */
  2435.         /* process the next object on the touched chain.                                                                        */
  2436.         
  2437.         /* We need to generate update if we have delete old objects either in the new                */
  2438.         /* container or the most recent updating container.                                                                    */
  2439.         if ((touch != NULL) || deletedObject || deletedRecently) {/* if somthing to generate*/
  2440.             
  2441.             /* If this is the first deletion generation, create the TOC #1 "deletes" property    */
  2442.             /* IN THE PRIVATE TOC.  That's the TOC #1 object we're interested in.                            */
  2443.             
  2444.             if (deletesValueHdr == NULL) {                                        /* if this is the first delete    */
  2445.                 if ((deletesValueHdr = makeDeleteValue(container, ioBuffer)) == NULL) return (false);
  2446.             } /* 1st delete */
  2447.             
  2448.             /* Generate the old update instruction once again in the new merged container            */
  2449.             if ((targetContainer) && (deletedRecently))
  2450.                 generatePastDeleteUpdates(theObject, container, targetContainer, ioBuffer);
  2451.  
  2452.             /* We're now ready to generate update instructions for the current touched object.*/
  2453.             /* For a deleted object we have the sequence "<del obj> O" and that's all need be    */
  2454.             /* done for this object.  For deleted property we have "<del prop1> OP" the first */
  2455.             /* time for this object, and "<del prop2> P" for additional touched deleted             */
  2456.             /* properties on the touched list.                                                                                                */
  2457.             
  2458.             if (deletedObject) {                                                            /* if deleted object...                    */
  2459.                 PUT1(DeleteObject                , ioBuffer);                        /* <del obj>                                        */
  2460.                 PUT4(theObject->objectID, ioBuffer);                        /*            O                                    */
  2461.             } else if (touch) {                                                                /* if non-deleted object...            */
  2462.                 PUT1(DeleteProperty1        , ioBuffer);                        /* <del prop1>                                    */
  2463.                 PUT4(theObject->objectID, ioBuffer);                        /*            O                                    */
  2464.                 
  2465.                 first = true;                                                                        /* this will gen P the 1st time    */
  2466.                 
  2467.                 do {                                                                                        /* walk the touched list...            */
  2468.                     nextTouch = (TouchedListEntryPtr)cmGetNextListCell(touch);
  2469.                     
  2470.                     if ((touch->touchFlags & TouchedDeletedProperty) != 0) { /* deleted property?    */
  2471.                         if (!first)                                                                    /* new property...                            */
  2472.                             PUT1(DeleteProperty2, ioBuffer);                    /* <del prop2>                                    */
  2473.                         
  2474.                         PUT4(touch->propertyID, ioBuffer);                    /*             P                                */
  2475.                         first = false;
  2476.                     }
  2477.                     
  2478.                     /* Free everything remaining on the touched list just like we did for deleted    */
  2479.                     /* objects...                                                                                                                                    */
  2480.                     
  2481.                     if (touch->theValueHdr)
  2482.                         touch->theValueHdr->touch = NULL;
  2483.                     CMfree(cmDeleteListCell(&theObject->touchedList, touch));
  2484.     
  2485.                     touch = nextTouch;                                                        /* process all list entries            */
  2486.                 } while (touch != NULL);
  2487.             }
  2488.         } /* object or property deletions */
  2489.         
  2490.         theObject = theObject->nextTouchedObject;
  2491.     } /* while (theObject)... */
  2492.     
  2493.     /* If we are doing merging, then we may need to regenerate the delete object opcodes  */
  2494.     /* These objects are not on the touch list, since they don't even exists any more.        */
  2495.     /* So we need to go through the past history, get the object ID and generate the            */
  2496.     /* delete update once again in the merged container.                                                                    */
  2497.  
  2498.     if (targetContainer) {                                                                    /* we are doing merging                */
  2499.         pastUpdate = (PastUpdate *) cmGetListHead(&targetContainer->tmpList);
  2500.         while (pastUpdate) {
  2501.             next = (PastUpdate *) cmGetNextListCell(pastUpdate);
  2502.             if (pastUpdate->ctlByte == DeleteObject) {
  2503.                 if (deletesValueHdr == NULL)
  2504.                     if ((deletesValueHdr = makeDeleteValue(container, ioBuffer)) == NULL) 
  2505.                         return (false);
  2506.                 PUT1(DeleteObject, ioBuffer);                                         /* <OpCode>                                        */
  2507.                 PUT4(pastUpdate->objectID, ioBuffer);                            /*                 objectID                        */
  2508.                 CMfree(cmDeleteListCell(&targetContainer->tmpList, pastUpdate));
  2509.             }
  2510.             pastUpdate = next;
  2511.         }
  2512.     }    
  2513.  
  2514.     /* All deletion instructions (if any) have been generated.  All that remains to do is */
  2515.     /* write an "end- of-sequence" marker byte and to flush the buffer.                                        */
  2516.     
  2517.     if (deletesValueHdr) {                                                                /* if deletions generate...            */
  2518.         PUT1(EndUpdates, ioBuffer);                                                    /* add "end-of-update-sequence"    */
  2519.         cmFlushOutputBuffer(ioBuffer);                                            /* ...end-of-updates                        */
  2520.         /* if we merge two embedded containesr, we need to offset the new values                        */
  2521.         if (container->mergeInSize) {
  2522.             theValue = (TOCValuePtr)cmGetListHead(&deletesValueHdr->valueList);
  2523.             while (theValue) {
  2524.                 if ((theValue->flags & kCMImmediate) == 0)
  2525.                     theValue->value.notImm.value += container->mergeInSize;
  2526.                 theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  2527.             }
  2528.         }
  2529.     }
  2530.     
  2531.     return (true);                                                                                /* success                                            */
  2532. }
  2533.  
  2534.  
  2535. /*--------------------------------------------------------------------*
  2536.  | cmGenerateUpdates - main control for update instruction generation |
  2537.  *--------------------------------------------------------------------*
  2538.  
  2539.  This is called at close time (CMCloseContainer()) to generate all the updating
  2540.  instructions for all objects and their values touched during an updating session. True is
  2541.  returned if successful and false otherwise (and the error reporter returns).
  2542.  
  2543.  The touched objects are on the touched chain pointed to from the container control block.
  2544.  Two passes are made over the touched chain:
  2545.  
  2546.          Pass 1: All touched values are processed.  The touched list for each non-deleted 
  2547.                         object that is still on the touched chain (see pass 2) are processed for
  2548.                         touched values.  Touched properties are skipped.  
  2549.                         
  2550.                         A special "updates" property is generated for each object involved which 
  2551.                         contains a value whose data is all the value updates for that object.  Each
  2552.                         processed touched value's touched list entry is deleted.
  2553.                         
  2554.                         The end of pass 1 leaves only deleted property touched list entries in 
  2555.                         non-deleted objects on the touched chain and touched, but deleted, objects.
  2556.                         Deleted objects too can still have a touched chain of "removed" and "deleted
  2557.                         objects" since such entries are retained due to the way the move and deletion
  2558.                         touch algorithms work.
  2559.                         
  2560.         Pass 2: All touched deleted objects and deleted properties.  This is the remaining
  2561.                         stuff on the touched chain and lists.  The data for this is attached to a
  2562.                         special TOC #1 property.  The remaining touched list entries are deleted. 
  2563.                         Thus, at the end of pass 2, all touched list space is freed up.
  2564. */
  2565.  
  2566. CMBoolean cmGenerateUpdates(ContainerPtr container, ContainerPtr targetContainer)
  2567. {
  2568.     CMBoolean success;
  2569.     void            *ioBuffer = NULL;
  2570.     jmp_buf        genUpdatesEnv;
  2571.     
  2572.     container = container->updatingContainer;            /* make sure we're using updater                */
  2573.     
  2574.     if (setjmp(genUpdatesEnv)) {                                    /* set setjmp/longjmp environment vector*/
  2575.         Container_Disable(container);
  2576.         cmReleaseIOBuffer(ioBuffer);                                /* ...if longjmp taken back here...            */
  2577.         ERROR1(CM_err_BadUpdateWrite,CONTAINERNAME);/* ...free buffer and report failure        */
  2578.         return (false);                                                            /* ...false ==> failure                                    */
  2579.     }
  2580.     
  2581.     /* Allocate the output buffer to more efficiently generate the update data...                    */
  2582.     
  2583.     ioBuffer = cmUseIOBuffer(container, UpdateBufSize, (jmp_buf *)&genUpdatesEnv);
  2584.     if (ioBuffer == NULL) return (false);
  2585.     
  2586.     /* Process both passes...                                                                                                                            */
  2587.     
  2588.     /* First pass generate the value update, the second pass generate the delete updates.    */
  2589.     success = (CMBoolean)(generateValueUpdates(container, targetContainer, ioBuffer) &&
  2590.                                             generateDeleteUpdates(container, targetContainer, ioBuffer));
  2591.     /* Anything left over in the past history list (is there really any?) is discarded        */
  2592.     if (targetContainer)
  2593.         cmFreeAllListCells(&targetContainer->tmpList, container->sessionData);
  2594.     
  2595.     /* Either of the above routines returns "false" if they fail for any reason.  Either    */
  2596.     /* way we give back the buffer space and then return success or failure.                            */
  2597.     
  2598.     cmReleaseIOBuffer(ioBuffer);                                    /* say good bye to the output buffer        */
  2599.     
  2600.     return (success);                                                            /* true ==> continue close processing        */
  2601. }
  2602.  
  2603. #if debugUpdateList
  2604. /*--------------------------------------------*
  2605.  | dumpOPT - dump OPT to the specified file |
  2606.  *--------------------------------------------*
  2607.  
  2608.  This routine is used for internal debugging, print the OPT address to the (open) file 
  2609.  associated with the specified file variable, f.
  2610. */
  2611.  
  2612. static void CM_NEAR  dumpOPT(FILE CM_PTR *f,
  2613.                                                          const CM_CHAR *heading, 
  2614.                                                          CM_ULONG objectID,
  2615.                                                          ...)
  2616. {
  2617.     va_list     otherParams;
  2618.     CM_LONG     nextID;
  2619.     CM_SHORT    i;
  2620.     
  2621.     fprintf(f, "|%-13.13s| %.8lX |", heading, objectID);
  2622.     va_start (otherParams, objectID);
  2623.     for (i=0;i<6;i++)
  2624.         if ((nextID=va_arg(otherParams, long)) == -1)
  2625.             break;
  2626.         else
  2627.             fprintf(f, " %.8lX |", nextID);
  2628.     va_end (otherParams);
  2629.     fprintf(f, "\n");
  2630. }
  2631. #endif
  2632.  
  2633. /*---------------------------------------------------------------------*
  2634.  | makePastUpdate - put update instruction on past update history list |
  2635.  *---------------------------------------------------------------------*
  2636.  
  2637.  Put the update instruction in the past update history list for future
  2638.  use of merging.
  2639. */
  2640.  
  2641. static PastUpdate CM_NEAR *makePastUpdate(ContainerPtr container,
  2642.                                                                                     TOCObjectPtr theObject,
  2643.                                                                                     CM_ULONG propertyID,
  2644.                                                                                     CM_ULONG typeID,
  2645.                                                                                     CM_CHAR ctlByte,
  2646.                                                                                     CM_ULONG structSize)
  2647. {
  2648.     PastUpdate    *pastUpdate = NULL;
  2649.     
  2650.     if (container->updateMergeCandidate)
  2651.     {
  2652.         pastUpdate = (PastUpdate*) 
  2653.             cmAppendListCell(&container->tmpList, CMmalloc(structSize));
  2654.         if (pastUpdate)
  2655.         {
  2656.             if (ctlByte >= RemovedValue)
  2657.                 theObject->objectFlags |= EditedRecently;
  2658.             else if (ctlByte != DeleteObject)
  2659.                 theObject->objectFlags |= DeletedRecently;
  2660.             pastUpdate->objectID = theObject->objectID;
  2661.             pastUpdate->ctlByte = ctlByte;
  2662.             pastUpdate->propertyID = propertyID;
  2663.             pastUpdate->typeID = typeID;
  2664.             if (ctlByte != DeleteObject)
  2665.                 cmAddObjToTouchedChain(theObject->container->updatingContainer, theObject);
  2666.         }
  2667.     }
  2668.     return pastUpdate;
  2669. }
  2670.  
  2671. /*-----------------------------------------------------------------------*
  2672.  | applyValueUpdateInstructions - value updating instruction interpreter |
  2673.  *-----------------------------------------------------------------------*
  2674.  
  2675.  This routine is called only by applyValueUpdates() to apply value updating instructions.
  2676.  applyValueUpdates() calls this routine at the point in the sequence where it sees 
  2677.  "<v> [params...]", where <v> is the next control byte to be read in.  The property and
  2678.  type information have already been processed and is passed to this routine along with the
  2679.  target object refNum.  These uniquely address the value to be operated upon by the
  2680.  "<v> [params...]" instructions.  Also passed is the object's refNum special "updates"
  2681.  property value refNum from which the updating  "<v> [params...]" instructions are being
  2682.  read.
  2683.  
  2684.  Note, that there may be multiple sets of updating instructions for different values of
  2685.  the SAME property.  The type IDs change, while the property ID stays the same.  For 
  2686.  efficiency, we search for the object's property only when property IDs change. 
  2687.  applyValueUpdates() supplys a place to save the pointer, *theProperty, and it doubles as
  2688.  a switch indicating whether the property ID should be processed.  NULL indicates we need
  2689.  the property pointer.
  2690.  
  2691.  The "proper" amount of updating info is consumed (read in).  The "[params...]" is
  2692.  recursive in that it may itself contain "<v> [params...]" for additional updates to the
  2693.  same value.  Thus we read "ahead" here until an "unrecognizable" control byte is 
  2694.  encountered.  That control byte is returned as the function result.  If any errors are
  2695.  detected, a special "control", ErrorCtl, is returned.
  2696.  
  2697.  This routine is the symmetric counterpart to its generator, genValueUpdateInstructions().
  2698.  It interprets the updating instructions in the following formats:
  2699.  
  2700.                                             +---+
  2701.          RemovedValue     |   |
  2702.                                             +---+
  2703.                                     
  2704.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+
  2705.          InsertedValue    |   |  original O   |  original P   |  original T   |
  2706.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+
  2707.                                                                 
  2708.                                             +---+---+---+---+---+---+---+---+---+
  2709.          SetInfoedValue   |   |     new T     |   generation  |
  2710.                                             +---+---+---+---+---+---+---+---+---+
  2711.                                     
  2712.                                             +---+---+---+---+---+---+---+---+---+
  2713.          DeleteData       |   |starting offset|    amount     |
  2714.                                             +---+---+---+---+---+---+---+---+---+
  2715.                                     
  2716.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+
  2717.          InsertData1      |   | insert point  |  data offset  |  data length  |
  2718.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+
  2719.                                     
  2720.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  2721.          InsertDataN      |   | insert point  |       N       | data offset1  | data length1  |
  2722.                                             +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  2723.                                                                                                                                               - - -
  2724.                                                           +---+---+---+---+---+---+---+---+
  2725.                                                                                                                     | data offsetN  | data lengthN  |
  2726.                                                                                                                     +---+---+---+---+---+---+---+---+
  2727.  
  2728.                                             +---+---+---+---+---+---+
  2729.          ReplaceImmediate |   | l | new imm value |
  2730.           (length=l<=4)   +---+---+---+---+---+---+
  2731.                                     
  2732.                                             +---+---+---+---+---+---+---+       +---+---+---+---+
  2733.          ReplaceBaseType  |   |n bases| base type #1  | - - - | base type #n  |
  2734.                                             +---+---+---+---+---+---+---+       +---+---+---+---+
  2735.          
  2736.                                             +---+
  2737.          DeleteValue      |   |
  2738.                                             +---+
  2739.  
  2740.  See genValueUpdateInstructions() for more info on these sequences.
  2741.  
  2742.  For the purpose of merging, we want to keep a history of the update, we shall keep that
  2743.  in a list. We only need to keep the history of the most recent updating container.
  2744.  
  2745.  The list will be kept in the tmpList field of the container. This field is only used
  2746.  in the updating container, so we can overload its use.
  2747. */
  2748.  
  2749. static CM_UCHAR CM_NEAR applyValueUpdateInstructions(ContainerPtr container,
  2750.                                                                                                          TOCObjectPtr theObject,
  2751.                                                                                                          TOCPropertyPtr *theProperty,
  2752.                                                                                                          CM_ULONG propertyID,
  2753.                                                                                                          CM_ULONG typeID,
  2754.                                                                                                          void *ioBuffer)
  2755. {
  2756.     CM_UCHAR               ctlByte;
  2757.     CM_ULONG           fromObjectID, fromPropertyID, fromTypeID, newTypeID, newImmediate,
  2758.                                  baseTypeID, offset, size, endOffset, n, insertPoint, segOffset;
  2759.     CM_USHORT             nBases;
  2760.     TOCObjectPtr      theFromObject, newTypeObject, theToProperty;
  2761.     TOCPropertyPtr theFromProperty;
  2762.     TOCValueHdrPtr theFromValueHdr, theValueHdr;
  2763.     TOCValuePtr         theValue, theNextValue, theInsertValue, insBeforeValue;
  2764.     CMGeneration      newGeneration;
  2765.     TOCValueBytes     valueBytes;
  2766.     CMBoolean             gotData, reLogicalize;
  2767.     CM_CHAR              errStr[15], errStr1[15], errStr2[15];
  2768.     PastUpdate          *pastUpdate;
  2769.     PastInsertData *pastInsert;
  2770.     OffsetSizePair *offsetInfoPtr;
  2771.     CM_ULONG             *baseTypePtr;
  2772.             
  2773.     /* When applyValueUpdates() calls this routine, it only has extracted the property         */
  2774.     /* and type IDs for the target value.  It did nothing else.  The reason is because of */
  2775.     /* moves, i.e., "inserted" and "removed" updating instruction entries.                                */
  2776.     
  2777.     /* For "inserted" entries, the target object, i.e., the receiver (i.e., destination)     */
  2778.     /* property for the move, does NOT have to exist prior to applying the "inserted"         */
  2779.     /* instruction for that object!  For "removed" the last value in a source objects's     */
  2780.     /* property could have been moved out, thus deleting the property in that object.            */
  2781.     /* Either way, there is the potential that the property being acted upon does not         */
  2782.     /* exist.                                                                                                                                                            */
  2783.     
  2784.     /* In all other cases, there is no value movement, so the property does have to exist.*/
  2785.     /* But applyValueUpdates() doesn't know whether the <v> in the "<v> [params...]"             */
  2786.     /* sequence is for a move or not.  As a matter of fact, at this point, neither do we! */
  2787.     /* So we can't set up the target value refNum until we know for sure what we have as    */
  2788.     /* the first control byte.                                                                                                                        */
  2789.     
  2790.     /* If it is "inserted", it is guaranteed to be first the first in the sequence by         */
  2791.     /* genValueUpdateInstructions().  This is done so that possible additional editing        */
  2792.     /* operations have a value in the target object/property to work on.  The move will        */
  2793.     /* cause the value refNum to be set up.                                                                                                */
  2794.     
  2795.     /* If the control byte is "removed", it will also be the first of a sequence because    */
  2796.     /* "removed" entries are never combined with any other.      We never try to set or use    */
  2797.     /* the value refNum for this since, in this implementation, "removed" entries are            */
  2798.     /* completely ignored.                                                                                                                                */
  2799.     
  2800.     /* As soon as we know e don't have "inserted" or "removed" we can set the target value*/
  2801.     /* refNum.  Since we do not yet know the first value instruction, we set a switch,         */
  2802.     /* appropriately enough, the value (hdr) refNum, to indicate we need the value refNum.*/
  2803.     /* In the processing loop we set the value refNum at the "proper" time. That will be    */
  2804.     /* the first time for anything except moves or after the move is done.                                */
  2805.     
  2806.     theValueHdr = NULL;                                                                        /* don't have target value yet    */
  2807.     
  2808.     /* Insert and delete updates change the data. As described in value update generation    */
  2809.     /* routine, generateDataEdits(), the algorithm for determining whether NEW inserts and*/
  2810.     /* deletes occurred at close time requires that the segment's logical offsets reflect */
  2811.     /* a contiguous sequence, from 0 to the logical size.  As "old" (i.e., previous)             */
  2812.     /* updates are applied to the target container (which is what we're doing here), the     */
  2813.     /* logical offsets and size, as set when the TOC was loaded, are invalidated. Thus at */
  2814.     /* the end of updating this value, if any inserts or deletes occured, we must                 */
  2815.     /* "re-logicalize" (do you like that verb?) the updates segments.  To that end, the     */
  2816.     /* switch, reLogicalize, is maintained and tested to see if this must be done.                */
  2817.  
  2818.     reLogicalize = false;                                                                    /* assume no data edits                    */
  2819.     
  2820.     /* Process "<v> [params...]" sequences.  The "[params...]" is  recursive in that it     */
  2821.     /* may itself contain "<v> [params...]" for additional updates to the same value.         */
  2822.     /* Thus we loop here until we hit a "<v>" that is not valid in this context.                  */
  2823.     
  2824.     for (;;) {                                                                                        /* process each sequence...            */
  2825.         ctlByte = GET1(ioBuffer);                                                        /* read in next "<v>" ctl byte    */
  2826.         
  2827.         /* Now that we have a control byte, we can ask if this is the first one for the            */
  2828.         /* value, and if we have an "inserted" (move) entry.  If it's not a move, we can        */
  2829.         /* set up the value refNum.  Otherwise, we wait!  The property is only searched for */
  2830.         /* only when the property ID changes.  For updates to multiple values for the same    */
  2831.         /* property, it will not change.  applyValueUpdates() indicates we have a new value    */
  2832.         /* by setting *theProperty to NULL. We set *theProperty here. As long as it reamins    */
  2833.         /* non-NULL there is no need to process the property ID.                                                        */
  2834.         
  2835.         if (theValueHdr == NULL          &&                                         /* if no value refNum yet...        */
  2836.               ctlByte != InsertedValue &&                                            /* ...and not "inserted" entry..*/
  2837.                 ctlByte != RemovedValue) {                                            /* ...and not "removed" entry...*/
  2838.             if (*theProperty == NULL) {                                                /* ...get property if  changed    */
  2839.                 *theProperty = cmGetObjectProperty(theObject, propertyID);
  2840. #if !CMIGNORE_NONFATAL
  2841.                 if (*theProperty == NULL) {
  2842.                     Container_Disable(container);
  2843.                     ERROR4(CM_err_NoUpdateProperty, CONTAINERNAME,
  2844.                                  cmltostr(propertyID, 1, false, errStr),
  2845.                                  cmltostr(theObject->objectID, 1, false, errStr1),
  2846.                                  "updated");
  2847.                     return (ErrorCtl);
  2848.                 }
  2849. #endif
  2850.             }
  2851.             
  2852. #if CMIGNORE_NONFATAL
  2853.             if (*theProperty)
  2854. #endif
  2855.             theValueHdr = cmGetPropertyType(*theProperty,typeID);    /* get a new type...                */
  2856. #if !CMIGNORE_NONFATAL
  2857.             if (theValueHdr == NULL) {
  2858.                 Container_Disable(container);
  2859.                 ERROR4(CM_err_NoUpdateType, CONTAINERNAME,
  2860.                              cmltostr(typeID, 1, false, errStr),
  2861.                              cmltostr(theObject->objectID, 1, false, errStr1),
  2862.                              cmltostr(propertyID, 1, false, errStr2));
  2863.                 return (ErrorCtl);
  2864.             }
  2865. #endif
  2866.         }
  2867.         
  2868.         /* The value refNum (theValueHdr) is now set if we don't have a "inserted" entry.     */
  2869.         /* The "inserted" entry will set it after it does the move.  The move will be first    */
  2870.         /* if it's there.  Either way we have a value refNum which is the receiver of the     */
  2871.         /* actions determined by the updating instructions we're about to process...                */
  2872.  
  2873.         switch (ctlByte) {                                                                    /* act upon the control byte...    */
  2874.         
  2875.                                                                  /*--------------*
  2876.                                                                   | RemovedValue |
  2877.                                                                   *--------------*
  2878.             
  2879.             A "removed" instruction is provided for random access implementation that can
  2880.             explicitly access the TOC.  Its counterpart is the "inserted value" instruction. 
  2881.             Thus a random access implementation knows to remove the value from the object it's
  2882.             in and to insert it in the one with the "inserted value" instruction.
  2883.             
  2884.             In this implementation we load the entire TOC into memory.  Therefore, a "inserted
  2885.             value" maps into a "move" and the "removed" instruction maps into a NOP so that the
  2886.             move has a source.
  2887.             
  2888.             Since a "removed" instruction can be the only instruction for the value if it is
  2889.             indeed generated, we can exit and return the next byte, should should be a control
  2890.             byte.                                                                                                                                                            */
  2891.                                                                     
  2892.             case RemovedValue:                                                                /* "removed" value...                        */
  2893.                 #if CMDUMPTOC && debugUpdateList                                 /* show updating instructions    */
  2894.                 if (SESSION->cmDbgFile)                                                             /* ...if debugging                */
  2895.                     dumpOPT(SESSION->cmDbgFile, "Removed Value", theObject->objectID, propertyID, typeID, -1);
  2896.                 #endif
  2897.                 /* We do not merge RemovedValue udpate in this version                                                    */
  2898.                 container->updateMergeCandidate = false;
  2899.                 return (GET1(ioBuffer));                                                /* ...exit with next byte                */
  2900.                 
  2901.                                                                   /*-------------*
  2902.                                                                    | DeleteValue |
  2903.                                                                    *-------------*
  2904.                                                                      
  2905.             A "deleted value" instruction requires that the target value be deleted.  As with
  2906.          "removed" there can be no other instructions for the value, so again we can exit
  2907.           and return the next byte as the control byte.                                                                             */
  2908.             
  2909.             case DeleteValue:                                                                    /* "deleted value"...                        */
  2910.                 #if CMDUMPTOC && debugUpdateList                                 /* show updating instructions    */
  2911.                 if (SESSION->cmDbgFile)                                                             /* ...if debugging                */
  2912.                     dumpOPT(SESSION->cmDbgFile, "Delete Value", theObject->objectID, propertyID, typeID, -1);
  2913.                 #endif
  2914.                 if (theValueHdr) {
  2915.                     cmMarkValueDeleted(container, theValueHdr, true);
  2916.                     /* put the DeleteValue on the update history list                                                                */
  2917.                     (void) makePastUpdate(container, theObject, propertyID, typeID, DeleteValue, 
  2918.                                                                 sizeof(PastUpdate));
  2919.                 }
  2920.                 return (GET1(ioBuffer));                                                /* ...exit with next byte                */
  2921.  
  2922.                                                                  /*---------------*
  2923.                                                                   | InsertedValue |
  2924.                                                                   *---------------*
  2925.             
  2926.             An "inserted value" in this implementation (see "removed" above) means MOVE the
  2927.             value to the current location from the source defined by the instruction's
  2928.             operands.  We do the the move just like CMMoveValue() does it, by calling the common
  2929.             routine cmMoveValueHdr().   Before doing that we have to do some work to convert
  2930.             the operands, in the form of object IDs to object refNums.  We never trust anything
  2931.             as we do this!
  2932.             
  2933.             Since this is not used in OpenDoc we have have look into the case of ignoring the
  2934.             non-fatal error for InsertedValue                                                                                                    */
  2935.             
  2936.             case InsertedValue:                                                                /* "inserted" value...                    */
  2937.                 fromObjectID      = (CM_ULONG)GET4(ioBuffer);    /* ...get source IDs                        */
  2938.                 fromPropertyID = (CM_ULONG)GET4(ioBuffer);
  2939.                 fromTypeID          = (CM_ULONG)GET4(ioBuffer);
  2940.                 #if CMDUMPTOC && debugUpdateList                                 /* show updating instructions    */
  2941.                 if (SESSION->cmDbgFile)                                                             /* ...if debugging                */
  2942.                     dumpOPT(SESSION->cmDbgFile, "Inserted Value", theObject->objectID, propertyID, 
  2943.                                     typeID, fromObjectID, fromPropertyID, fromTypeID, -1);
  2944.                 #endif
  2945.                 
  2946.                 /* Convert the "from" object ID to its object refNum...                                                    */
  2947.                 
  2948.                 theFromObject = cmFindObject(container->toc, fromObjectID);
  2949.                 if (theFromObject == NULL) {
  2950.                     Container_Disable(container);
  2951.                     ERROR3(CM_err_NoUpdateObject, CONTAINERNAME,
  2952.                                  cmltostr(fromObjectID, 1, false, errStr), "moved");
  2953.                     return (ErrorCtl);
  2954.                 }
  2955.                 
  2956.                 /* Find the property with the object...                                                                                    */
  2957.                 
  2958.                 theFromProperty = cmGetObjectProperty(theFromObject, fromPropertyID);
  2959.                 if (theFromProperty == NULL) {
  2960.                     Container_Disable(container);
  2961.                     ERROR4(CM_err_NoUpdateProperty, CONTAINERNAME,
  2962.                                  cmltostr(fromPropertyID, 1, false, errStr),
  2963.                                  cmltostr(fromObjectID, 1, false, errStr1),
  2964.                                  "moved");
  2965.                     return (ErrorCtl);
  2966.                 }
  2967.                 
  2968.                 /* Find the value with the desired type ID in the "from" object's property.         */
  2969.                 /* This yields the "from" (source) value refNum to move.                                                */
  2970.                 
  2971.                 theFromValueHdr = cmGetPropertyType(theFromProperty, fromTypeID);
  2972.                 if (theFromValueHdr == NULL) {
  2973.                     Container_Disable(container);
  2974.                     ERROR4(CM_err_NoUpdateType, CONTAINERNAME,
  2975.                                  cmltostr(fromTypeID, 1, false, errStr),
  2976.                                  cmltostr(fromPropertyID, 1, false, errStr1),
  2977.                                  cmltostr(fromObjectID, 1, false, errStr2));
  2978.                     return (ErrorCtl);
  2979.                 }
  2980.                 
  2981.                 /* Finally, we need the refNum of the target property...                                                */
  2982.                 
  2983.                 theToProperty = cmFindObject(container->toc, propertyID);
  2984.                 if (theToProperty == NULL) {
  2985.                     Container_Disable(container);
  2986.                     ERROR4(CM_err_UndefUpdateObject, CONTAINERNAME, "property",
  2987.                                  cmltostr(theValueHdr->theProperty->propertyID, 1, false, errStr),
  2988.                                  "move");
  2989.                     return (ErrorCtl);
  2990.                 }
  2991.                 
  2992.                 /* Move the "from" value to the "to" (current target) object and property. This    */
  2993.                 /* potentially brings the property for the object into existence if this is the    */
  2994.                 /* first reference to the property for that object. The property object itslef, */
  2995.                 /* of course, must already exist.  That's the check we did just above.                    */
  2996.                 
  2997.                 theValueHdr = cmMoveValueHdr(theFromValueHdr, (CMObject)theObject, (CMProperty)theToProperty);
  2998.                 if (theValueHdr == NULL) return (ErrorCtl);            /* must have value refNum now        */
  2999.                 *theProperty = theValueHdr->theProperty;                /* set the property for value        */
  3000.                 /* We do not merge InsertedValue udpate in this version                                                    */
  3001.                 container->updateMergeCandidate = false;
  3002.                 
  3003.                 continue;                                                                                /* look for more updates                */
  3004.                 
  3005.                                                                 /*----------------*
  3006.                                                                  | SetInfoedValue |
  3007.                                                                  *----------------*
  3008.             
  3009.             Change the type and generation for the target value.  The instruction parameters 
  3010.             contain the new values.                                                                                                                        */
  3011.             
  3012.             case SetInfoedValue:                                                            /* "set-infoed" value...                */
  3013.                 newTypeID             = (CM_ULONG)GET4(ioBuffer);     /* ...get new values                        */
  3014.                 #if TOC1_SUPPORT
  3015.                 if (container->majorVersion == 1)
  3016.                     newGeneration = (CM_ULONG)GET2(ioBuffer);
  3017.                 else
  3018.                     newGeneration = (CM_ULONG)GET4(ioBuffer);
  3019.                 #else
  3020.                 newGeneration = (CM_ULONG)GET4(ioBuffer);
  3021.                 #endif
  3022.                 #if CMDUMPTOC && debugUpdateList                                 /* show updating instructions    */
  3023.                 if (SESSION->cmDbgFile)                                                             /* ...if debugging                */
  3024.                     dumpOPT(SESSION->cmDbgFile, "SetInfoedValue", theObject->objectID, propertyID, 
  3025.                                     typeID, newGeneration, newTypeID, -1);
  3026.                 #endif
  3027.                 
  3028.                 /* Validate that the type object exists...                                                                            */
  3029.                 
  3030.                 newTypeObject = cmFindObject(container->toc, newTypeID);
  3031.                 if (newTypeObject == NULL) {
  3032.                     Container_Disable(container);
  3033.                     ERROR4(CM_err_UndefUpdateObject, CONTAINERNAME, "type",
  3034.                                  cmltostr(newTypeID, 1, false, errStr),
  3035.                                  "set-info");
  3036.                     return (ErrorCtl);
  3037.                 }
  3038.                 
  3039.                 /* Must have a legal generation number too...                                                                        */
  3040.                 
  3041.                 if (newGeneration < 1) {
  3042.                     Container_Disable(container);
  3043.                     ERROR2(CM_err_UpdateBadGenNbr, CONTAINERNAME, 
  3044.                                  cmltostr(newGeneration, 1, false, errStr));
  3045.                     return (ErrorCtl);
  3046.                 }
  3047.                 
  3048.                 if (theValueHdr) {
  3049.                     /* We do not merge value type change in this version                                                    */
  3050.                     if (theValueHdr->typeID != newTypeID)
  3051.                         container->updateMergeCandidate = false;
  3052.                     else {
  3053.                         /* put the SetInfoedValude on the update history list                                                */
  3054.                         pastUpdate = makePastUpdate(container, theObject, propertyID, typeID,
  3055.                                                                                 SetInfoedValue, sizeof(PastSetInfoedValue));
  3056.                         if (pastUpdate) {
  3057.                             ((PastSetInfoedValue *)pastUpdate)->newType = (CMObjectID)newTypeID;
  3058.                             ((PastSetInfoedValue *)pastUpdate)->generation = newGeneration;
  3059.                         }
  3060.                     }
  3061.     
  3062.                     theValueHdr->generation = newGeneration;                /* set new generation number    */
  3063.                     theValueHdr->typeID = (CMObjectID)newTypeID;        /* ...and type ID                            */
  3064.                 }
  3065.                                 
  3066.                 continue;                                                                                /* look for more updates                */
  3067.                 
  3068.                                                                 /*------------*
  3069.                                                                  | DeleteData |
  3070.                                                                    *------------*
  3071.                                                                      
  3072.             Delete data from a value.  This will cause "re-logicalizing" at the end.  The
  3073.             instruction parameters contain the offset from where to start deleting and the size 
  3074.             size of the deletion.  The deletion is done just as CMDeleteValueData() does it, by
  3075.             calling    the common routine cmDeleteSegmentData().                                                                    */
  3076.             
  3077.             case DeleteData:                                                                    /* "delete data"...                            */
  3078.                 offset = (CM_ULONG)GET4(ioBuffer);                            /* ...get deletion parameters..    */
  3079.                 size     = (CM_ULONG)GET4(ioBuffer);
  3080.                 #if CMDUMPTOC && debugUpdateList                                 /* show updating instructions        */
  3081.                 if (SESSION->cmDbgFile)                                                             /* ...if debugging                    */
  3082.                     dumpOPT(SESSION->cmDbgFile, "DeleteData", theObject->objectID, propertyID, 
  3083.                                     typeID, 0, offset, size, -1);
  3084.                 #endif
  3085.                 
  3086.                 if (theValueHdr) {        
  3087.                     endOffset   = offset + size - 1;                                /* ...get ending offset                */
  3088.                     if (endOffset >= theValueHdr->size) 
  3089.                         endOffset = theValueHdr->size - 1;
  3090.                     
  3091.                     cmDeleteSegmentData(theValueHdr, offset, endOffset); /* delete the data                */
  3092.                     
  3093.                     /* put the DeleteData on the update history list                                                            */
  3094.                     pastUpdate = makePastUpdate(container, theObject, propertyID, typeID,
  3095.                                                                             DeleteData, sizeof(PastDeleteData));
  3096.                     if (pastUpdate) {
  3097.                         ((PastDeleteData*)pastUpdate)->offset = offset;
  3098.                         ((PastDeleteData*)pastUpdate)->size = endOffset - offset + 1;
  3099.                     }
  3100.                     reLogicalize = true;                                                        /* this requires re-logicalizing*/
  3101.                 }
  3102.                 continue;                                                                                /* look for more updates                */
  3103.             
  3104.  
  3105.                                                          /*-------------------------*
  3106.                                                             | InsertData1/InsertDataN |
  3107.                                                             *-------------------------*
  3108.             
  3109.             Both data insertion cases are handled here.  The case of a single insertion is the
  3110.             same as for multi-segment insertions except the count is always 1.  The insertion
  3111.             is done by inserting the first segment just as CMInsertValueData() does it.  The
  3112.             common routine cmInsertNewSegment() is used to insert that first segment.  Here,
  3113.             we're given the offset and length to a segment.  CMInsertValueData(), on the other
  3114.             hand, must write the data passed to it by the user.  Either way the data is written
  3115.             by the time cmInsertNewSegment() is called.  It handles worrying about splitting the
  3116.             segment being inserted into if necessary.
  3117.             
  3118.             Once the first segment is inserted, all the other segments for a multi-segment 
  3119.             insert are simply linked in following the new segment.
  3120.             
  3121.             Having said all this, there is one special case.  That is the case of "inserting" 
  3122.             to the end of the current value (i.e., an append operation).  In that case all we
  3123.             need to do is append the new segment(s) to the target value.
  3124.             
  3125.             Since NEW segments for a value are being generated here, these segments must be     
  3126.             tagged, like all others, with the container that "owns" them.  That is the passed
  3127.             container.  Unfortunatley, standard segment creation uses the value header to get
  3128.             at updatingContainer.  In the normal case, updatingContainer points to the container
  3129.             to the top-most updater to receive new updates.  But at the time we're applying
  3130.             updates, we don't want that container because we're not necessarily at the top yet.
  3131.             So we have to retag the segment with the current container.
  3132.             
  3133.             Like delete, inserts cause "re-logicalizing" at the end.                                                    */
  3134.             
  3135.             case InsertData1:                                                                    /* insert 1 new segment...            */
  3136.             case InsertDataN:                                                                    /* insert N new segments...            */
  3137.                 
  3138.                 /* Get the insert point and number of inserts, n.  For InsertData1, n is 1.            */
  3139.                 
  3140.                 insertPoint = (CM_ULONG)GET4(ioBuffer);
  3141.                 n                        = (ctlByte == InsertData1) ? 1UL : (CM_ULONG)GET4(ioBuffer);
  3142.                 if (n == 0) {
  3143.                     Container_Disable(container);
  3144.                     ERROR1(CM_err_BadInsertData, CONTAINERNAME);
  3145.                     return (ErrorCtl);
  3146.                 }
  3147.                 
  3148.                 /* If we're apppending the data instead of actually inserting it, then just         */
  3149.                 /* prepare to append new segments to the end of the existing ones.  If it is a    */
  3150.                 /* true insertion, then we must process the first segment specially. It must be    */
  3151.                 /* inserted just like CMInsertValueData() would do it.                                                    */
  3152.             
  3153.                 if (theValueHdr) {                
  3154.                     offset = (CM_ULONG)GET4(ioBuffer);                            /* get 1st seg params...            */
  3155.                     size      = (CM_ULONG)GET4(ioBuffer);
  3156.                 
  3157.                     /* put the InsertData on the update history list                                                            */
  3158.                     pastInsert = (PastInsertData *)makePastUpdate(container, 
  3159.                                                                                                                 theObject, 
  3160.                                                                                                                 propertyID, 
  3161.                                                                                                                 typeID, 
  3162.                                                                                                                 ctlByte,
  3163.                                                                                                                 sizeof(PastInsertData)+(n-1)*2*sizeof(CM_ULONG));
  3164.                     if (pastInsert) {
  3165.                         pastInsert->insertPoint = insertPoint;
  3166.                         pastInsert->insertCount = n;
  3167.                         pastInsert->offset = offset;
  3168.                         pastInsert->size = size;
  3169.                         offsetInfoPtr = (OffsetSizePair *)(++pastInsert);
  3170.                     }
  3171.                     else
  3172.                         offsetInfoPtr = NULL;
  3173.     
  3174.                     #if CMDUMPTOC && debugUpdateList                                 /* show updating instructions    */
  3175.                     if (SESSION->cmDbgFile)                                                             /* ...if debugging                */
  3176.                         dumpOPT(SESSION->cmDbgFile, "InsertData", theObject->objectID, propertyID, 
  3177.                                         typeID, insertPoint, offset, size, -1);
  3178.                     #endif
  3179.                     
  3180.                     if (insertPoint == theValueHdr->size) {                    /* if appending...                        */
  3181.                         gotData               = true;                                                /* indicate we have 1st params*/
  3182.                         theInsertValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList);
  3183.                     } else {                                                                                /* if inserting...                        */
  3184.                         
  3185.                         /* Just as in CMInsertValueData(), find out which existing segment we're going*/
  3186.                         /* to insert into and the offset within that segment...                                            */
  3187.                         
  3188.                         insBeforeValue  = cmGetStartingValue(theValueHdr, insertPoint, &segOffset);
  3189.                         if (insBeforeValue == NULL) {
  3190.                             Container_Disable(container);
  3191.                             ERROR2(CM_err_BadInsertOffset, CONTAINERNAME, cmltostr(insertPoint, 1, false, errStr));
  3192.                             return (ErrorCtl);
  3193.                         }
  3194.                         
  3195.                         theInsertValue = cmInsertNewSegment(theValueHdr, insBeforeValue, segOffset, offset, size);
  3196.                         if (theInsertValue == NULL) return (ErrorCtl);
  3197.                         
  3198.                         theInsertValue->container = container;            /* set proper owner of this seg    */
  3199.                         
  3200.                         --n;                                                                                /* count 1st segment processed    */
  3201.                         gotData = false;                                                        /* we need to read if more segs    */
  3202.                     }
  3203.                     
  3204.                     /* The remaining segments (if any) are just inserted after the one inserted     */
  3205.                     /* above.  If we're just appeneding, all the segments are handled here.                */
  3206.                     
  3207.                     while (n--) {                                                                    /* insert/append remaining segs    */
  3208.                         if (!gotData) {                                                            /* get i'th segment params...        */
  3209.                             offset = (CM_ULONG)GET4(ioBuffer);
  3210.                             size   = (CM_ULONG)GET4(ioBuffer);
  3211.                             if (offsetInfoPtr) {
  3212.                                 offsetInfoPtr->offset = offset;
  3213.                                 offsetInfoPtr->size = size;
  3214.                                 offsetInfoPtr++;
  3215.                             }
  3216.                             #if CMDUMPTOC && debugUpdateList                         /* show updating instructions    */
  3217.                             if (SESSION->cmDbgFile)                                                     /* ...if debugging                */
  3218.                                 dumpOPT(SESSION->cmDbgFile, "InsertData", theObject->objectID, propertyID, 
  3219.                                                 typeID, insertPoint, offset, size, -1);
  3220.                             #endif
  3221.                         }
  3222.                         
  3223.                         if ((theValueHdr->size == 0) && (theInsertValue)) {
  3224.                             /* special case, segment with empty value, just set it                                        */
  3225.                             theValueHdr->valueFlags &= ~ValueImmediate;    /* it is no longer an immediate    */
  3226.                             theValue = theInsertValue;
  3227.                             theValue->value.notImm.value         = (CM_ULONG)offset;
  3228.                             theValue->value.notImm.valueLen = (CM_ULONG)size;
  3229.                             theValue->flags = 0;
  3230.                             theValue->container = container;                    /* set proper owner of this seg    */
  3231.                         } else {
  3232.                             /* Create a new value segment using the updating offset/size values...        */
  3233.                             
  3234.                             (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, offset, size);
  3235.                             theValue = cmCreateValueSegment(theValueHdr, &valueBytes, 0);
  3236.                             if (theValue == NULL) return (ErrorCtl);
  3237.                             
  3238.                             theValue->container = container;                    /* set proper owner of this seg    */
  3239.                             
  3240.                             /* Insert/append the new segment and set the associated flags and value        */
  3241.                             /*  header size...                                                                                                                */
  3242.                             
  3243.                             cmInsertAfterListCell(&theValueHdr->valueList, theValue, theInsertValue);
  3244.                             
  3245.     #if CMKEEP_CONTINUE_FLAG
  3246.                             theInsertValue->flags = (CM_USHORT)(theInsertValue->flags | kCMContinued);
  3247.                             theValueHdr->valueFlags |= ValueContinued;
  3248.     #endif
  3249.                         }
  3250.                         
  3251.                         theValueHdr->size += size;
  3252.                         gotData                  = false;                                            /* need info for each new seg        */                                    
  3253.                         theInsertValue = theValue;                                    /* set to insert next segment        */
  3254.                     } /* while */
  3255.                     
  3256.                     reLogicalize = true;                                                    /* this requires re-logicalizing*/
  3257.                 }
  3258. #if CMIGNORE_NONFATAL
  3259.                 else {
  3260.                     while (n--) {
  3261.                         (void)GET4(ioBuffer);
  3262.                         (void)GET4(ioBuffer);
  3263.                     }
  3264.                 }
  3265. #endif
  3266.                 continue;                                                                                /* look for more updates                */
  3267.                 
  3268.                                                                 /*------------------*
  3269.                                                                  | ReplaceImmediate |
  3270.                                                                  *------------------*
  3271.                                                                  
  3272.             Any immediate that was edited in a previous session and was recored as an update at
  3273.             close time is treated specially.  Rather than recording individual updates, the    
  3274.             entire immediate is output as a "replace immediate" update instruction.  That
  3275.             replacement is to be done now (at open time).     Replacing non-immediate by immediate
  3276.             means that the old are deleted and then replaced by the new immediate data.
  3277.             
  3278.             Note, re-logicalizing is currently not necessary since there can be only one
  3279.             segment for continued values...                                                                                                        */
  3280.             
  3281.             case ReplaceImmediate:                                                        /* "replace immediate"...                */
  3282.                 if (theValueHdr) {
  3283.                     theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  3284.                     if ((theValue->flags & kCMImmediate) == 0) {
  3285.                         /* if version is less than 2.1, then make it 2.1                                                        */
  3286.                         if (container->minorVersion < MinorVersion)
  3287.                             container->minorVersion = MinorVersion;
  3288.                         if (cmCountListCells(&theValueHdr->valueList) != 1) {
  3289.                             cmFreeAllListCells(&theValueHdr->valueList, SESSION);
  3290.                             (void)cmSetValueBytes(container, &valueBytes, Value_Imm_Chars, 0, 0);
  3291.                             cmAppendValue(theValueHdr, &valueBytes, kCMImmediate);
  3292.                             theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  3293.                         } else {
  3294.                             theValue->flags = kCMImmediate;
  3295.                             theValueHdr->valueFlags &= ~ValueImmediate;
  3296.                         }
  3297.                     
  3298.                     }
  3299.                     size                   = (CM_ULONG)GET1(ioBuffer);                 /* get new size                                 */
  3300.                     newImmediate = (CM_ULONG)GET4Direct(ioBuffer);     /* and immediate                                */
  3301.                     
  3302.                     #if CMDUMPTOC && debugUpdateList                                 /* show updating instructions        */
  3303.                     if (SESSION->cmDbgFile)                                                             /* ...if debugging                    */
  3304.                         dumpOPT(SESSION->cmDbgFile, "ReplaceImmediate", theObject->objectID, propertyID, 
  3305.                                         typeID, 0, newImmediate, size, -1);
  3306.                     #endif
  3307.                     
  3308.                     theValue->value.notImm.valueLen = size;                    /* replace the immediate...            */
  3309.                     theValue->value.imm.ulongValue    = newImmediate;
  3310.                     theValueHdr->size                             = size;
  3311.                     theValueHdr->logicalSize                 = size;
  3312.                     /* put the ReplaceImmediate on the update history list                                                    */
  3313.                     pastUpdate = makePastUpdate(container, theObject, propertyID, typeID,
  3314.                                                                             ReplaceImmediate, sizeof(PastReplaceImmediate));
  3315.                     if (pastUpdate) {
  3316.                         ((PastReplaceImmediate*)pastUpdate)->size = size;
  3317.                         ((PastReplaceImmediate*)pastUpdate)->newImmediate = newImmediate;
  3318.                     }
  3319.                 }
  3320. #if CMIGNORE_NONFATAL
  3321.                 else {
  3322.                     (void)GET1(ioBuffer);                 /* get new size                                 */
  3323.                     (void)GET4Direct(ioBuffer);     /* and immediate                                */
  3324.                 }
  3325. #endif
  3326.                 continue;                                                                                /* look for more updates                */
  3327.  
  3328.                                                                  /*-----------------*
  3329.                                                                   | ReplaceBaseType |
  3330.                                                                   *-----------------*
  3331.                                                                     
  3332.             A replaced base type is similar to a replaced immediate.  Base type values are 
  3333.             segments of immediate data wich each immediate being a base type ID.  The entire
  3334.             base type is replaced with the values from the updating instruction's parameters.
  3335.             A ssafety check here is that the referenced value is indeed a base type.                    */
  3336.             
  3337.             case ReplaceBaseType:                                                            /* replace base type...                    */
  3338.                 /* There are N base type parameters.  Knowing that we know how to set the value    */
  3339.                 /* header info...                                                                                                                                */
  3340.                 
  3341.                 nBases = (CM_USHORT)GET2(ioBuffer);                         /* get nbr of bases                            */
  3342.                 
  3343. #if CMIGNORE_NONFATAL
  3344.                 if (!theValueHdr) {
  3345.                     while (nBases--)                                                            /* skip over the unused ones         */                                                            
  3346.                         (void) GET4(ioBuffer);                
  3347.                 }
  3348.                 else {
  3349. #endif
  3350.                 if (theValueHdr->typeID != CM_StdObjID_BaseTypeList) {
  3351.                     Container_Disable(container);
  3352.                     ERROR1(CM_err_CantRepBaseType, CONTAINERNAME);
  3353.                     return (ErrorCtl);
  3354.                 }
  3355.                 
  3356. #if CMKEEP_CONTINUE_FLAG
  3357.                 if (nBases > 1)                                                                 /* set echoed "continued" bit        */
  3358.                     theValueHdr->valueFlags |= ValueContinued;        /* set if N > 1                                    */
  3359.                 else
  3360.                     theValueHdr->valueFlags &= ~ValueContinued;        /* reset if N = 1                                */
  3361. #endif
  3362.                 theValueHdr->size = nBases * sizeof(CMObjectID);/* set new total size                        */
  3363.                 
  3364.                 /* put the ReplaceBaseType on the update history list                                                        */
  3365.                 pastUpdate = makePastUpdate(container, theObject, propertyID, typeID,
  3366.                                                                         ReplaceBaseType, 
  3367.                                                                         sizeof(PastReplaceBaseType)+sizeof(CM_ULONG)*nBases);
  3368.                 if (pastUpdate) {
  3369.                     ((PastReplaceBaseType *)pastUpdate)->nBases = nBases;
  3370.                     baseTypePtr = (CM_ULONG *)((char *)pastUpdate + sizeof(PastReplaceBaseType));;
  3371.                 }
  3372.  
  3373.                 /* replace existing segments with the new base type values as long as there are    */
  3374.                 /* enough segments.  After that we must create new segments.                                        */
  3375.                     
  3376.                 theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  3377.                 
  3378.                 while (nBases--) {                                                            /* create N segments...                    */
  3379.                     baseTypeID = (CM_ULONG)GET4(ioBuffer);                /* get next base ID                            */
  3380.                     #if CMDUMPTOC && debugUpdateList                                 /* show updating instructions    */
  3381.                     if (SESSION->cmDbgFile)                                                             /* ...if debugging                */
  3382.                         dumpOPT(SESSION->cmDbgFile, "ReplaceBaseType", theObject->objectID, propertyID, 
  3383.                                         typeID, baseTypeID, -1);
  3384.                     #endif
  3385.  
  3386.                     if (theValue == NULL) {                                                /* create seg if no more to use    */
  3387.                         cmSetValueBytes(container, &valueBytes, Value_Imm_Long, baseTypeID, sizeof(CMObjectID));
  3388.                         theValue = cmAppendValue(theValueHdr, &valueBytes, kCMImmediate);
  3389.                         theValue->container = container;
  3390. #if CMKEEP_CONTINUE_FLAG
  3391.                         if (nBases > 0) theValue->flags |= kCMContinued;
  3392. #endif
  3393.                         theValue = NULL;
  3394.                     } else {                                                                            /* replace existing base type     */
  3395.                         theValue->value.imm.ulongValue = baseTypeID;
  3396. #if CMKEEP_CONTINUE_FLAG
  3397.                         if (nBases > 0) 
  3398.                             theValue->flags |= kCMContinued;
  3399.                         else
  3400.                             theValue->flags &= ~kCMContinued;
  3401. #endif
  3402.                         theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  3403.                     }
  3404.                     
  3405.                     /* remember each of the individual base type ID    in the past history                        */
  3406.                     if (pastUpdate) {
  3407.                         *baseTypePtr = baseTypeID;
  3408.                         baseTypePtr++;
  3409.                     }
  3410.                 }
  3411.                 
  3412.                 /* If the replacement used less segments than what was originally there, delete    */
  3413.                 /* the excess segments...                                                                                                                */
  3414.                 
  3415.                 while (theValue != NULL) {                                            /* delete excess segments...        */
  3416.                     theNextValue = (TOCValuePtr)cmGetNextListCell(theValue);
  3417.                     CMfree(cmDeleteListCell(&theValueHdr->valueList, theValue));
  3418.                     theValue = theNextValue;
  3419.                 }
  3420.  
  3421. #if CMIGNORE_NONFATAL
  3422.                 }                                                                                             /* theValueHdr not null                    */
  3423. #endif
  3424.                 continue;                                                                                /* look for more updates                */
  3425.  
  3426.                                                             /*----------------------*
  3427.                                                              | Unrecognized Control |
  3428.                                                              *----------------------*
  3429.             
  3430.             An unrecognized control byte has been encountered.  If any editing was done to the
  3431.             data, the value must be "re-logicalized".  The unrecognized control byte is 
  3432.             returned to applyValueUpdates() to process.                                                                                */
  3433.             
  3434.             default:                                                                                    /* unrecognized control byte...    */
  3435.                 if (reLogicalize) {                                                            /* if we must "re-logicalize"...*/
  3436.                     size = 0;                                                                            /* ...do it...                                    */
  3437.                     theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  3438.                 
  3439.                     while (theValue) {                                                        /* set new logical offsets            */
  3440.                         theValue->logicalOffset = size;
  3441.                         size += theValue->value.notImm.valueLen;
  3442.                         theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  3443.                     }
  3444.                     
  3445.                     theValueHdr->logicalSize = theValueHdr->size = size; /* set new logical size    */
  3446.                 }
  3447.             
  3448.                 return (ctlByte);                                                                /* return unrecognized ctl byte    */
  3449.         } /* switch */
  3450.     } /* for */
  3451. }
  3452.  
  3453.  
  3454. /*-----------------------------------------------------------------------------------*
  3455.  | applyValueUpdates - main control interpreter to apply value updating instructions |
  3456.  *-----------------------------------------------------------------------------------*
  3457.  
  3458.  This routine is called only by cmApplyUpdates() to control the interpretation of value
  3459.  updating instructions.  All objects that contain an updating property were put on the
  3460.  touched chain as the TOC was read.  Here we walk that chain and process that property.
  3461.  For each chained object, the updating property has a value whose data is all the updating
  3462.  instructions for its object.
  3463.  
  3464.  False is returned for failure, and true for success.  Read errors will longjmp back to
  3465.  cmApplyUpdates().
  3466.  
  3467.  This routine is the symmetric counterpart to its generator, generateValueUpdates().  It
  3468.  generates the updating instructions in the following sequence:
  3469.  
  3470.                                        --                                    --
  3471.                                                                        |  ( <new prop> P T <v> [params...] )  |
  3472.         <new prop> P T <v> [params...]  |  <                                >  | ... <end>
  3473.                                           |  ( <new type> T <v> [params...]   )  |
  3474.                                       --                                    --
  3475.  
  3476.  See generateValueUpdates() for the notation and meaning.
  3477. */
  3478.  
  3479. static CMBoolean CM_NEAR applyValueUpdates(ContainerPtr container, void *ioBuffer)
  3480. {
  3481.     TOCObjectPtr              theObject, nextTouchedObject;
  3482.     TOCPropertyPtr         theProperty;
  3483.     TOCValueHdrPtr         updatesValueHdr;
  3484.     CM_ULONG                     propertyID, typeID;
  3485.     register CM_UCHAR ctlByte;
  3486.     CM_CHAR                      errStr[15];
  3487.     
  3488.     /* Walk the entire touched chain built of objects because each such object contains a    */
  3489.     /* special "updates" property detected when the TOC was read in.                                            */
  3490.     
  3491.     theObject = container->touchedChain;                                /* point to 1st touched object         */
  3492.     
  3493.     while (theObject != NULL) {                                                    /* walk touched chain...                    */
  3494.         nextTouchedObject = theObject->nextTouchedObject;    /* get ptr to next object early        */
  3495.         
  3496.         /* To make it easier and more efficient for us to get at the special property and        */
  3497.         /* its value header (which is what we're really after), when the TOC reader put the    */
  3498.         /* object on the touched chain, it set the object's refCon to point at the updating    */
  3499.         /* instructions value header.  That's what we use to read the instruction value         */
  3500.         /* data.                                                                                                                                                        */
  3501.         
  3502.         updatesValueHdr = (TOCValueHdrPtr)theObject->objectRefCon; /* "updates" value hdr        */
  3503.     
  3504.         if (updatesValueHdr != NULL) {                                        /* the test is just for safety!        */
  3505.             cmNewBufferedInputData(ioBuffer, updatesValueHdr, updatesValueHdr->size);
  3506.             theProperty = NULL;                                                            /* NULL means haven't processed ID*/
  3507.         
  3508.             /* At this point we have one set of updating instructions for one object.  Here     */
  3509.             /* we expect property and type changes.  The object, of course, we know.  The            */
  3510.             /* SPECIFIC value updating interpretation is done separately, just like it was         */
  3511.             /* generated, by genValueUpdateInstructions().                                                                        */
  3512.             
  3513.             ctlByte = GET1(ioBuffer);                                                /* read ahead in this "scan"            */
  3514.             
  3515.             while (ctlByte != EndUpdates)                                     /* stop at <end>                                    */
  3516.                 switch (ctlByte) {                                                        /* process <new prop>/<new  type>    */
  3517.                     
  3518.                     /* The "<new prop> P T" merges with the "<new type> T" case to get the type     */
  3519.                     /* ID ("T").  Both these cases can be seen first in the sequence.                            */
  3520.                     
  3521.                     case NewProperty:                                                        /* <new prop>                                            */
  3522.                         propertyID = (CM_ULONG)GET4(ioBuffer);        /*           P                                        */
  3523.                         theProperty = NULL;
  3524.                         
  3525.                                           /* FALL THROUGH to NewType case */
  3526.                     
  3527.                     /* Handle the "<new type> T" case for multiple types for the same property,        */
  3528.                     /* and complete the "<new prop> P T" case falling into here from above.  This    */
  3529.                     /* yields the property/type pair to uniquely address a target value for the        */
  3530.                     /* current object.                                                                                                                        */
  3531.                     
  3532.                     case NewType:                                                                /* <new type>                                            */
  3533.                         typeID = (CM_ULONG)GET4(ioBuffer);                /*                     T                                        */
  3534.                         
  3535.                         /* A "<v> [params...]" sequence should always follow "<new prop> P T" and        */
  3536.                         /* "<new type> T".  These sequences are processed separately by the routine    */
  3537.                         /* applyValueUpdateInstructions().  The "proper" amount of updating info is */
  3538.                         /* consumed (read in). The "[params...]" are recursive in that it may             */
  3539.                         /* itself contain "<v> [params...]" for additional updates to the same             */
  3540.                         /* value. Thus applyValueUpdateInstructions() will be reading "ahead" until */
  3541.                         /* it finds an "unrecognizable" control byte it returns that here.  So we        */
  3542.                         /* already have the next control byte to process on return. This should be  */
  3543.                         /* either <new prop>, <new type>, or <end>. If any errors are detected, a     */
  3544.                         /* special "control", ErrorCtl, is returned so we know to quit.                            */
  3545.                         
  3546.                         ctlByte = applyValueUpdateInstructions(container, theObject, &theProperty, 
  3547.                                                                                                      propertyID, typeID, ioBuffer);
  3548.                         if (ctlByte == ErrorCtl) return (false);
  3549.                         
  3550.                         break;                                                                        /* process next control                        */
  3551.                     
  3552.                     default:                                                                        /* huh?                                                        */
  3553.                         Container_Disable(container);
  3554.                         ERROR3(CM_err_BadUpdateControl, CONTAINERNAME, 
  3555.                                      cmltostr(ctlByte, -2, true, errStr), "updating");
  3556.                         return (false);
  3557.                 } /* switch, while */
  3558.                 
  3559.                 /* updatesValueHdr is the update instruction of this object. If we are going    */
  3560.                 /* to merge the container later, we shall be generating a new updating                    */
  3561.                 /* instruction, and at that time we can release the space occupied by it.                */
  3562.                 /* So we put it in the temporary free list in case a merge would occur                    */
  3563.                 if (container->updateMergeCandidate)
  3564.                     cmAddValueToTmpFreeList(updatesValueHdr);
  3565.  
  3566.                 /* All the updating instructions for the current object have now been processed.*/
  3567.                 /* We now have to free the property so that a new one can be generated at close    */
  3568.                 /* time if this object happens to receive new updates.  Besides, why horde all     */
  3569.                 /* that wonderful memory?                                                                                                                */
  3570.                 
  3571.                 cmDeleteProperty(container, updatesValueHdr->theProperty);
  3572.         } /* updatesValueHdr */
  3573.         
  3574.         /* At this point we're done updating this object (unless it gets deleted by phase     */
  3575.         /* 2 -- applyDeleteUpdates()).  The object is removed from the touched chain and its*/
  3576.         /* refCon reset to NULL.  By the time we process the entire touched chain, it will    */
  3577.         /* end up being empty ready for recording NEW updates, if any.                                            */
  3578.         
  3579.         cmDeleteObjFromTouchedChain(container, theObject, NULL);
  3580.     
  3581.         theObject->objectRefCon = (CMRefCon)NULL;                    /* put refCon back to orig state    */
  3582.         theObject = nextTouchedObject;                                        /* process next touched object        */
  3583.     } /* while (theObject)... */
  3584.     
  3585.     return (true);                                                                            /* we actually made it!                        */
  3586. }
  3587.  
  3588.  
  3589. /*-------------------------------------------------------------------------------------*
  3590.  | applyDeleteUpdates - interpreter to apply object and property deletion instructions |
  3591.  *-------------------------------------------------------------------------------------*
  3592.  
  3593.  This routine is called only by cmApplyUpdates() to control the interpretation of object
  3594.  and property deletion updating instructions.  The deletesValueHdr is the refNum for the
  3595.  TOC #1 deletes property, if one is present.  If there are no deletions, it will be NULL. 
  3596.  
  3597.  False is returned for failure, and true for success.  Read errors will longjmp back to
  3598.  cmApplyUpdates().
  3599.  
  3600.  This routine is the symmetric counterpart to its generator, generateDeleteUpdates().  It
  3601.  generates the updating instructions in the following sequence:
  3602.  
  3603.         ( <del obj> O                         )
  3604.         <                                     > ... <end>
  3605.         ( <del prop1> O P [<del prop2> P ...] )
  3606.  
  3607.  See generateDeleteUpdates() for the notation and meaning.  It is also described there
  3608.  why the deletions are kept separate and must be processed last.
  3609. */
  3610.  
  3611. static CMBoolean CM_NEAR applyDeleteUpdates(ContainerPtr container, void *ioBuffer)
  3612. {
  3613.     TOCObjectPtr            theObject;
  3614.     TOCPropertyPtr        theProperty;
  3615.     TOCValueHdrPtr         deletesValueHdr = container->deletesValueHdr;
  3616.     register CM_UCHAR ctlByte;
  3617.     CMObjectID                 objectID, propertyID;
  3618.     void                            *toc;
  3619.     CM_CHAR                      errStr[15];
  3620. #if !CMIGNORE_NONFATAL
  3621.     CM_CHAR                      errStr1[15];
  3622. #endif
  3623.     
  3624.     if (deletesValueHdr == NULL)                                                /* exit if nothing to delete            */
  3625.         return (true);
  3626.         
  3627.     toc = container->toc;                                                                /* make things a little faster!        */
  3628.     theObject = NULL;                                                                        /* NULL to safety check this            */
  3629.     
  3630.     /* Process the updating instruction data of the TOC #1 "deletes" property...                    */
  3631.     
  3632.     cmNewBufferedInputData(ioBuffer, deletesValueHdr, deletesValueHdr->size);
  3633.  
  3634.     while ((CMBoolean)((ctlByte = GET1(ioBuffer)) != EndUpdates))
  3635.         switch (ctlByte) {
  3636.             
  3637.             /* Both "<del obj> O" and "<del prop1> O P" expect an object ID ("O") first. Thus    */
  3638.             /* both cases are processed together for the O.  Both these cases can be seen         */
  3639.             /* first in the sequence.                                                                                                                    */
  3640.             
  3641.             case DeleteProperty1:                                                        /* <del prop1>                                         */
  3642.             case DeleteObject:                                                            /* <del obj>                                              */
  3643.                 objectID  = (CMObjectID)GET4(ioBuffer);                /*          O                                            */
  3644.                 theObject = cmFindObject(toc, objectID);
  3645.                 
  3646.                 /* If only "<del obj> O" then just delete the object and go get next control        */
  3647.                 /* byte.  Otherwise we still need the "P" for the "<del prop1> O P" case.                */
  3648.                 
  3649.                 if (ctlByte == DeleteObject) {                                /* <del obj> O ==> just delete obj*/
  3650.                     if (theObject == NULL) {
  3651. #if CMIGNORE_NONFATAL
  3652.                         break;
  3653. #else
  3654.                         Container_Disable(container);
  3655.                         ERROR3(CM_err_NoUpdateObject, CONTAINERNAME,
  3656.                                      cmltostr(objectID, 1, false, errStr), "deleted");
  3657.                         return (false);
  3658. #endif
  3659.                     }
  3660.                     #if CMDUMPTOC && debugUpdateList                                 /* show updating instructions    */
  3661.                     if (SESSION->cmDbgFile)                                                             /* ...if debugging                */
  3662.                         dumpOPT(SESSION->cmDbgFile, "Delete Object", objectID, -1);
  3663.                     #endif
  3664.                     /* put the delete instruction on the past update history for use in merge            */
  3665.                     (void) makePastUpdate(container, theObject, 0, 0, DeleteObject, sizeof(PastUpdate));
  3666.                     cmMarkObjectDeleted(container, theObject);
  3667.                     break;                                                                            /* process next control byte            */
  3668.                 }
  3669.             
  3670.                                                 /* FALL THROUGH to DeleteProperty2 case */
  3671.             
  3672.             /* Get the property ID ("P") for the "<del prop1> O P" case or completely process    */
  3673.             /* the "<del prop2> P" case for additional properties for the same object. In          */
  3674.             /* both these cases the property for the current object is deleted.  Note that a     */
  3675.             /* "<del prop2> P" case should never be first in the deletion sequence, i.e., we     */
  3676.             /* better have an object.                                                                                                                    */
  3677.             
  3678.             case DeleteProperty2:                                                        /* <del prop2>                                         */
  3679.                 if (theObject == NULL) {
  3680. #if CMIGNORE_NONFATAL
  3681.                     break;
  3682. #else
  3683.                     Container_Disable(container);
  3684.                     ERROR3(CM_err_NoUpdateObject, CONTAINERNAME, "?", "deleted");
  3685.                     return (false);
  3686. #endif
  3687.                 }
  3688.                 
  3689.                 propertyID = (CMObjectID)GET4(ioBuffer);            /*            P                                        */
  3690.                 theProperty = cmGetObjectProperty(theObject, propertyID);
  3691.                 if (theProperty == NULL) {
  3692. #if CMIGNORE_NONFATAL
  3693.                     break;
  3694. #else
  3695.                     Container_Disable(container);
  3696.                     ERROR4(CM_err_NoUpdateProperty, CONTAINERNAME,
  3697.                                  cmltostr(propertyID, 1, false, errStr),
  3698.                                  cmltostr(objectID, 1, false, errStr1),
  3699.                                  "deleted");
  3700.                     return (false);
  3701. #endif
  3702.                 }
  3703.                 /* put the delete instruction on the past update history for use in merge                */
  3704.                 (void) makePastUpdate(container, theObject, propertyID, 0, DeleteProperty1, 
  3705.                                                             sizeof(PastUpdate));
  3706.                 #if CMDUMPTOC && debugUpdateList                                     /* show updating instructions    */
  3707.                 if (SESSION->cmDbgFile)                                                             /* ...if debugging                */
  3708.                     dumpOPT(SESSION->cmDbgFile, "Delete Property", objectID, propertyID, -1);
  3709.                 #endif
  3710.                 cmDeleteProperty(container, theProperty);            /* delete property from its object*/
  3711.                 break;                                                                                /* process next control byte            */
  3712.             
  3713.             default:                                                                                /* huh?                                                        */
  3714.                 Container_Disable(container);
  3715.                 ERROR3(CM_err_BadUpdateControl, CONTAINERNAME, 
  3716.                              cmltostr(ctlByte, -2, true, errStr), "deleting");
  3717.                 return (false);
  3718.         } /* switch, while */
  3719.     
  3720.         /* deletesValueHdr is the deleteion update instruction of this object. If we are         */
  3721.         /* going to merge the container later, we shall be generating a new updating                */
  3722.         /* instruction, and at that time we can release the space occupied by it.                        */
  3723.         /* So we put it in the temporary free list in case a merge would occur                            */
  3724.     if (container->updateMergeCandidate)
  3725.         cmAddValueToTmpFreeList(deletesValueHdr);
  3726.  
  3727.     /* All the deletions have been done.  The TOC #1 property memory is freed  since it     */
  3728.     /* is not needed any longer.                                                                                                                    */
  3729.     
  3730.     cmDeleteProperty(container, deletesValueHdr->theProperty);
  3731.     container->deletesValueHdr = NULL;
  3732.  
  3733.     return (true);                                                                            /* incredible? isn't it?                    */
  3734. }
  3735.  
  3736.  
  3737. /*-------------------------------------------------------------------*
  3738.  | cmApplyUpdates - apply all updates to bring a target "up to date" |
  3739.  *-------------------------------------------------------------------*
  3740.  
  3741.  This is called at open time (openTargetContainer()) to apply (execute) all the updating
  3742.  instructions for all objects and their values "touched" while loading in the non-private
  3743.  portion of an updating container's TOC.  True is returned if successful and false
  3744.  otherwise (and the error reporter returns).
  3745.  
  3746.  The touched objects are on the touched chain pointed to from the passed container control
  3747.  block.  These were objects containing a special "updates" property.  Such a property has
  3748.  a value whose value data is all the updating instructions dealing with that object.  The
  3749.  reason they were chained was so that we could get at the objects with that property and
  3750.  do it at a time when we know we have all the objects read in.  During the read there could
  3751.  be references in the instructions for as-yet unread objects.
  3752.  
  3753.  To make it easier (and more efficient) to get at the "updates" property value for the
  3754.  objects on the touched chain, such objects have had their refCon set with the refNum
  3755.  (pointer) of the updating instruction value header.  That was done at the same time the
  3756.  object was placed on the touched chain.  We'll clear it to NULL as we process those
  3757.  objects.
  3758.  
  3759.  This routine is more-or-less the symmetric counterpart to cmGenerateUpdates() above,
  3760.  which generated the instructions we're about to read here.  That generation is done in
  3761.  two passes to generate two groups of updates; first the value updates, and then the 
  3762.  object/property deletions.  The value updates are the instructions attached to each
  3763.  object and that is what caused the touched chain to be built that we are going to process
  3764.  here.  The deletions are attached to a special TOC #1 "deletes" property.  We process
  3765.  those AFTER the value updates are processed (more about this below).
  3766.  
  3767.  Thus, rather than two passes over the touched chain that we do in generating the updates,
  3768.  we have two phases here:
  3769.  
  3770.          Phase 1: All updating instructions are processed for each object on the touched chain.
  3771.                          This will update all the values appropriately.  Since the updating
  3772.                          instructions are in terms of the original "OPT addresses" of the values we
  3773.                          know how to find them.  Moves can be made.
  3774.                          
  3775.                          Each object processed is removed from the touched chain.  Thus at the end of
  3776.                          phase 1, the touched chain is empty and ready to receive REAL update touches.
  3777.                         
  3778.         Phase 2: The "deletes" TOC #1 property is used to get the deletion updates list.  All
  3779.                          object and property deletes are done at this time.  This must be done last
  3780.                          in order for phase 1 to work correctly. The problem is with moves interacting
  3781.                          with deletes.  If a value is moved from A to B, and then A (its original
  3782.                          location) deleted, then the updates to that value would not have A to refer 
  3783.                          to. We always need the ORIGINAL "OPT" addresses of the values.
  3784.                          
  3785.  A note about the container and updatingContainer pointers...
  3786.  
  3787.  At the time the non-private TOC was loaded, and when we're called here, the container
  3788.  pointer is obviously the container that just received new values and whose updates are 
  3789.  to be applied to a target container.  The "target" takes the form of the TOC which 
  3790.  everyone is using at this point.  In order to have tagged new values with this container
  3791.  and in order to be able to get at the touched chain, the updatingContainer pointer is
  3792.  temporarily set to be the SAME as the container pointer, i.e., it points to its own
  3793.  container.  This is fully described in openTargetContainer().  It's sufficient to know
  3794.  that in the context of applying these updates, the container and updatingContainer
  3795.  pointers are equal in here.
  3796. */
  3797.  
  3798. CMBoolean cmApplyUpdates(ContainerPtr container)
  3799. {
  3800.     CMBoolean          success;
  3801.     CM_USHORT          savedUseFlags;
  3802.     void                     *ioBuffer = NULL;
  3803.     jmp_buf                 applyUpdatesEnv;
  3804.     
  3805.     /* Make sure there's something to do!  We must have either or both a touched chain         */
  3806.     /* and deletion list.  The deletion list "refNum" is saved in deletesValueHdr.                */
  3807.     
  3808.     if (container->touchedChain == NULL && container->deletesValueHdr == NULL)
  3809.         return (true);
  3810.         
  3811.     #if CMDUMPTOC && debugUpdateList                                 /* show updating instructions    */
  3812.     if (SESSION->cmDbgFile)                                                             /* ...if debugging                */
  3813.         fprintf(SESSION->cmDbgFile, "\nupdates in (%s)...\n\n", CONTAINERNAME);
  3814.     #endif
  3815.  
  3816.     /* "Adjust" the world to allow us to apply updates to a target container that opened    */
  3817.     /* for reading.  "Adjusting" here means that we pretend the container was opened for    */
  3818.     /* writing.  Further, we do not track free space created by deletion updates, and we     */
  3819.     /* free the refNums from those deletions rather than putting them keeping track of        */
  3820.     /* them as we normally do (to guard against user reuse, which we obviously don't have    */
  3821.     /* here).                                                                                                                                                            */
  3822.     
  3823.     savedUseFlags = container->useFlags;                    /* save container useFlags                            */
  3824.     container->useFlags |= kCMWriting;                        /* set opened for writing to update            */
  3825.     container->trackFreeSpace = false;                        /* stop free space tracking                            */
  3826.     cmChangeRefNumHandling(container->toc, false);/* allow freeing of obj/value refNums     */
  3827.     
  3828.     /* Rather than check for read failures on every read, a setjmp/longjmp environment is    */
  3829.     /* set up.  The buffered read routine, readUpdate(), will longjmp back here if it         */
  3830.     /* detects and reports an error and the error reporter returns.                                                */
  3831.     
  3832.     if (setjmp(applyUpdatesEnv)) {                                /* set setjmp/longjmp environment vector*/
  3833.         cmReleaseIOBuffer(ioBuffer);                                /* ...read err if longjmp taken...            */
  3834.         container->useFlags = savedUseFlags;                /* ...restore the original useFlags            */
  3835.         container->trackFreeSpace = true;                        /* ...track free space again                        */
  3836.         cmChangeRefNumHandling(container->toc,true);/* ...restore refNum tracking                        */
  3837.         Container_Disable(container);
  3838.         ERROR1(CM_err_BadUpdateRead, CONTAINERNAME);/* ...report error                                            */
  3839.         return (false);                                                            /* ...I bet we have a pissed off user!    */
  3840.     }
  3841.     
  3842.     /* Allocate the input buffer to more efficiently load the update data...                            */
  3843.     
  3844.     ioBuffer = cmUseIOBuffer(container, UpdateBufSize, (jmp_buf *)&applyUpdatesEnv);
  3845.     
  3846.     /* Process both phases...                                                                                                                            */
  3847.     
  3848.     if (ioBuffer != NULL)
  3849.         success = (CMBoolean)(applyValueUpdates(container, ioBuffer) &&    /* Phase 1: values        */
  3850.                                                 applyDeleteUpdates(container, ioBuffer));    /* Phase 2: deletions    */
  3851.     else
  3852.         success = false;
  3853.         
  3854.     /* Either of the above routines returns "false" if they fail for any reason (excluding*/
  3855.     /* read failures, which take the setjmp/longjmp exit above).  Either way we give back */
  3856.     /* the buffer space, undo the "damage" we did, and then return success or failure.        */
  3857.     
  3858.     cmReleaseIOBuffer(ioBuffer);                                    /* say good bye to the input buffer            */
  3859.     container->useFlags = savedUseFlags;                    /* restore the original useFlags                */
  3860.     container->trackFreeSpace = true;                            /* reenable free space tracking                    */
  3861.     cmChangeRefNumHandling(container->toc, true);    /* restore refNum tracking                            */
  3862.  
  3863.     return (success);                                                            /* true ==> continue close processing        */
  3864. }
  3865.                                                           
  3866.                                                             CM_END_CFUNCTIONS
  3867.